index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. <template>
  2. <div v-loading="pageLoading" class="app-container" :element-loading-text="loadingText">
  3. <div v-if="hasEnterprise">
  4. <PageSearch ref="searchRef" :search-config="searchConfig" @query-click="handleQueryClick"
  5. @reset-click="handleResetClick" />
  6. <PageContent ref="contentRef" :content-config="contentConfig">
  7. <template #toolbar="{ toolbarRight, onToolbar, removeIds, cols }">
  8. <CrudToolbarLeft v-if="!isAdmin" text="新增企业" :remove-ids="removeIds" :perm-create="['module_payment:enterprise:invite']">
  9. <el-button v-hasPerm="['module_payment:enterprise:invite']" type="primary" icon="Plus"
  10. @click="handleOpenDialog('apply')">
  11. 新增企业
  12. </el-button>
  13. </CrudToolbarLeft>
  14. <div class="data-table__toolbar--right">
  15. <CrudToolbarRight :buttons="toolbarRight" :cols="cols" :on-toolbar="onToolbar" />
  16. </div>
  17. </template>
  18. <template #table="{ data, loading, tableRef, onSelectionChange }">
  19. <div class="data-table__content">
  20. <el-table :ref="tableRef as any" v-loading="loading" :data="data" height="100%" border
  21. @selection-change="onSelectionChange">
  22. <template #empty>
  23. <el-empty :image-size="80" description="暂无数据" />
  24. </template>
  25. <el-table-column v-if="contentCols.find((col) => col.prop === 'selection')?.show" type="selection"
  26. min-width="55" align="center" />
  27. <el-table-column v-if="contentCols.find((col) => col.prop === 'enterprise_id')?.show" key="enterprise_id"
  28. label="企业ID" prop="enterprise_id" min-width="150" show-overflow-tooltip />
  29. <el-table-column v-if="contentCols.find((col) => col.prop === 'name')?.show" key="name" label="企业名称"
  30. prop="name" min-width="150" show-overflow-tooltip />
  31. <el-table-column v-if="contentCols.find((col) => col.prop === 'short_name')?.show" key="short_name"
  32. label="企业简称" prop="short_name" min-width="100" show-overflow-tooltip />
  33. <el-table-column v-if="contentCols.find((col) => col.prop === 'status')?.show" key="status" label="状态"
  34. prop="status" min-width="100">
  35. <template #default="scope">
  36. <el-tag :type="STATUS_TAG_TYPE[scope.row.status]">
  37. {{ STATUS_LABEL[scope.row.status] || scope.row.status }}
  38. </el-tag>
  39. </template>
  40. </el-table-column>
  41. <el-table-column v-if="contentCols.find((col) => col.prop === 'created_time')?.show" key="created_time"
  42. label="创建时间" prop="created_time" min-width="160" sortable />
  43. <el-table-column v-if="contentCols.find((col) => col.prop === 'f2f_status')?.show" key="f2f_status"
  44. label="当面付" prop="f2f_status" min-width="100">
  45. <template #default="scope">
  46. <el-tag v-if="scope.row.f2f_status" :type="ORDER_STATUS_TAG_TYPE[scope.row.f2f_status]">
  47. {{ ORDER_STATUS_LABEL[scope.row.f2f_status] }}
  48. </el-tag>
  49. <span v-else style="color: #909399">未开通</span>
  50. </template>
  51. </el-table-column>
  52. <el-table-column v-if="contentCols.find((col) => col.prop === 'operation')?.show" fixed="right" label="操作"
  53. align="center" min-width="280">
  54. <template #default="scope">
  55. <el-button type="primary" size="small" link
  56. @click="handleOpenDetail(scope.row)">
  57. 详情
  58. </el-button>
  59. <el-button
  60. size="small" link
  61. :type="scope.row.transfer_enabled ? 'danger' : 'success'"
  62. :loading="scope.row._toggling"
  63. @click="handleToggleTransfer(scope.row)">
  64. {{ scope.row.transfer_enabled ? '关闭转账' : '开通转账' }}
  65. </el-button>
  66. <el-button v-if="!isAdmin && (!scope.row.f2f_status || scope.row.f2f_status === 'CLOSED')"
  67. v-hasPerm="['module_payment:facetoface:apply']" type="success" size="small" link
  68. @click="handleOpenF2fApply(scope.row)">
  69. 开通当面付
  70. </el-button>
  71. <el-button
  72. v-if="scope.row.f2f_status === 'PENDING_AUTH'"
  73. type="warning" size="small" link icon="Link"
  74. @click="handleGetAuthUrl(scope.row)">
  75. 授权
  76. </el-button>
  77. </template>
  78. </el-table-column>
  79. </el-table>
  80. </div>
  81. </template>
  82. </PageContent>
  83. </div>
  84. <div v-else>
  85. <div class="enterprise-empty">
  86. <div class="empty-content">
  87. <div class="empty-icon">
  88. <el-icon class="empty-icon-large">
  89. <OfficeBuilding />
  90. </el-icon>
  91. </div>
  92. <h3 class="empty-title">企业入驻 & 签约</h3>
  93. <p class="empty-desc">
  94. 还没有企业入驻企业,点击下方按钮开始申请
  95. </p>
  96. <el-button type="primary" size="large" @click="handleOpenDialog('apply')">
  97. <el-icon>
  98. <Plus />
  99. </el-icon>
  100. 申请入驻
  101. </el-button>
  102. </div>
  103. </div>
  104. </div>
  105. <InviteDialog v-model="inviteDialogVisible" :invite-data="inviteData" />
  106. <EnhancedDialog v-model="dialogVisible.visible" :title="dialogVisible.title" @close="handleCloseDialog">
  107. <template v-if="dialogVisible.type === 'detail'">
  108. <EnterpriseDetail :out-biz-no="currentOutBizNo" />
  109. </template>
  110. <template v-else>
  111. <EnterpriseForm ref="formRef" :type="dialogVisible.type" :enterprise-id="currentOutBizNo"
  112. @success="handleFormSuccess" />
  113. </template>
  114. <template #footer>
  115. <div class="dialog-footer">
  116. <el-button v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit"
  117. :disabled="formRef?.value?.isSubmitting">
  118. 提交
  119. </el-button>
  120. <el-button v-else type="primary" @click="handleCloseDialog">确定</el-button>
  121. <el-button @click="handleCloseDialog">取消</el-button>
  122. </div>
  123. </template>
  124. </EnhancedDialog>
  125. <!-- 当面付代开通弹窗 -->
  126. <EnhancedDialog v-model="f2fDialogVisible" title="开通当面付" width="520px" @close="handleCloseF2fDialog">
  127. <el-form ref="f2fFormRef" :model="f2fForm" :rules="f2fRules" label-width="130px">
  128. <el-form-item label="商户支付宝账号" prop="account">
  129. <el-input v-model="f2fForm.account" placeholder="支付宝账号或2088开头的pid" />
  130. </el-form-item>
  131. <el-form-item label="经营类目" prop="mcc_code">
  132. <el-select v-model="f2fForm.mcc_code" placeholder="请搜索选择经营类目" filterable clearable
  133. style="width: 100%">
  134. <el-option v-for="item in MCC_OPTIONS" :key="item.value" :label="item.label" :value="item.value" />
  135. </el-select>
  136. </el-form-item>
  137. <el-form-item label="联系人姓名" prop="contact_name">
  138. <el-input v-model="f2fForm.contact_name" placeholder="请输入联系人姓名" />
  139. </el-form-item>
  140. <el-form-item label="联系人手机号" prop="contact_mobile">
  141. <el-input v-model="f2fForm.contact_mobile" placeholder="11位手机号码" maxlength="11" />
  142. </el-form-item>
  143. <el-form-item label="联系人邮箱">
  144. <el-input v-model="f2fForm.contact_email" placeholder="选填" />
  145. </el-form-item>
  146. <el-form-item label="店铺门头照">
  147. <input type="file" accept="image/*" @change="handleShopSignBoardPicChange" />
  148. </el-form-item>
  149. <el-form-item label="店铺内景照">
  150. <input type="file" accept="image/*" @change="handleShopScenePicChange" />
  151. </el-form-item>
  152. <el-form-item label="备注">
  153. <el-input v-model="f2fForm.remark" type="textarea" :rows="2" placeholder="选填" />
  154. </el-form-item>
  155. </el-form>
  156. <template #footer>
  157. <el-button type="primary" :loading="f2fSubmitting" @click="handleSubmitF2f">提交</el-button>
  158. <el-button @click="handleCloseF2fDialog">取消</el-button>
  159. </template>
  160. </EnhancedDialog>
  161. <EnhancedDialog v-model="employeeDialogVisible" title="添加员工" @close="handleEmployeeDialogClose">
  162. <EmployeeForm ref="employeeFormRef" type="create" :enterprise-id="employeeDialogEnterpriseId"
  163. @success="handleEmployeeFormSuccess" />
  164. <template #footer>
  165. <div class="dialog-footer">
  166. <el-button type="primary" @click="handleEmployeeFormSubmit" :loading="isEmployeeSubmitting">
  167. 提交
  168. </el-button>
  169. <el-button @click="handleEmployeeDialogClose">取消</el-button>
  170. </div>
  171. </template>
  172. </EnhancedDialog>
  173. </div>
  174. </template>
  175. <script setup lang="ts">
  176. defineOptions({
  177. name: "Enterprise",
  178. inheritAttrs: false,
  179. });
  180. import { OfficeBuilding, Plus } from "@element-plus/icons-vue";
  181. import EnterpriseAPI, {
  182. STATUS_TAG_TYPE,
  183. STATUS_LABEL,
  184. } from "@/api/module_payment/enterprise";
  185. import FacetofaceAPI, {
  186. ORDER_STATUS_TAG_TYPE,
  187. ORDER_STATUS_LABEL,
  188. } from "@/api/module_payment/facetoface";
  189. import { MCC_OPTIONS } from "@/constants/mcc-codes";
  190. import CrudToolbarLeft from "@/components/CURD/CrudToolbarLeft.vue";
  191. import CrudToolbarRight from "@/components/CURD/CrudToolbarRight.vue";
  192. import PageSearch from "@/components/CURD/PageSearch.vue";
  193. import PageContent from "@/components/CURD/PageContent.vue";
  194. import EnhancedDialog from "@/components/CURD/EnhancedDialog.vue";
  195. import EnterpriseForm from "./components/EnterpriseForm.vue";
  196. import EnterpriseDetail from "./components/EnterpriseDetail.vue";
  197. import InviteDialog from "./components/InviteDialog.vue";
  198. import EmployeeForm from "@/views/module_payment/employee/components/EmployeeForm.vue";
  199. import type { ISearchConfig, IContentConfig } from "@/components/CURD/types";
  200. import { useCrudList } from "@/components/CURD/useCrudList";
  201. import { useLoadingAction } from "@/composables/useLoadingAction";
  202. import { useRouter } from "vue-router";
  203. import { ElMessage } from "element-plus";
  204. import { ref, reactive, computed } from "vue";
  205. import { useEnterpriseStore } from "@/store/modules/enterprise.store";
  206. import { useUserStore } from "@/store/modules/user.store";
  207. import { ROLE_ROOT } from "@/constants/storage-keys";
  208. const router = useRouter();
  209. const userStore = useUserStore();
  210. const isAdmin = computed(() => userStore.basicInfo?.roles?.some(r => r.code === ROLE_ROOT));
  211. const { searchRef, contentRef, handleQueryClick, handleResetClick, refreshList } = useCrudList();
  212. const formRef = ref();
  213. const enterpriseStore = useEnterpriseStore();
  214. const hasEnterprise = computed(() => enterpriseStore.hasEnterprise);
  215. onMounted(async () => {});
  216. const searchConfig = reactive<ISearchConfig>({
  217. permPrefix: "module_payment:enterprise",
  218. colon: true,
  219. isExpandable: true,
  220. showNumber: 2,
  221. form: { labelWidth: "auto" },
  222. formItems: [
  223. {
  224. prop: "name",
  225. label: "企业名称",
  226. type: "input",
  227. attrs: { placeholder: "请输入企业名称", clearable: true },
  228. },
  229. {
  230. prop: "enterprise_id",
  231. label: "企业ID",
  232. type: "input",
  233. attrs: { placeholder: "请输入企业ID", clearable: true },
  234. },
  235. {
  236. prop: "status",
  237. label: "状态",
  238. type: "select",
  239. options: [
  240. { label: "全部", value: "" },
  241. { label: "已创建", value: "ENTERPRISE_CREATE" },
  242. { label: "已签约", value: "ENTERPRISE_ACTIVATED" },
  243. { label: "已解约", value: "ENTERPRISE_UNSIGN" },
  244. { label: "已注销", value: "ENTERPRISE_WITHDRAW" },
  245. ],
  246. attrs: { placeholder: "请选择状态", clearable: true, style: { width: "167.5px" } },
  247. },
  248. {
  249. prop: "created_time",
  250. label: "创建时间",
  251. type: "date-picker",
  252. attrs: {
  253. type: "datetimerange",
  254. rangeSeparator: "至",
  255. startPlaceholder: "开始日期",
  256. endPlaceholder: "结束日期",
  257. format: "YYYY-MM-DD HH:mm:ss",
  258. valueFormat: "YYYY-MM-DD HH:mm:ss",
  259. style: { width: "340px" },
  260. },
  261. },
  262. ],
  263. });
  264. const contentCols = reactive<
  265. Array<{
  266. prop?: string;
  267. label?: string;
  268. show?: boolean;
  269. }>
  270. >([
  271. { prop: "selection", label: "选择框", show: false },
  272. { prop: "index", label: "序号", show: true },
  273. { prop: "name", label: "企业名称", show: true },
  274. { prop: "short_name", label: "企业简称", show: true },
  275. { prop: "enterprise_id", label: "企业ID", show: true },
  276. { prop: "status", label: "状态", show: true },
  277. { prop: "f2f_status", label: "当面付", show: true },
  278. { prop: "created_time", label: "创建时间", show: true },
  279. { prop: "operation", label: "操作", show: true },
  280. ]);
  281. const contentConfig = reactive<IContentConfig<any>>({
  282. permPrefix: "module_payment:enterprise",
  283. pk: "enterprise_id",
  284. cols: contentCols as IContentConfig["cols"],
  285. hideColumnFilter: false,
  286. toolbar: [],
  287. defaultToolbar: ["refresh", "filter"],
  288. pagination: true,
  289. request: { page_no: "page_no", page_size: "page_size" },
  290. indexAction: async (params) => {
  291. const search: Record<string, any> = {};
  292. if (params.name) search.name = params.name;
  293. if (params.enterprise_id) search.enterprise_id = params.enterprise_id;
  294. if (params.status) search.status = params.status;
  295. if (params.created_time && params.created_time.length === 2) {
  296. search.start_time = params.created_time[0];
  297. search.end_time = params.created_time[1];
  298. }
  299. const res = await EnterpriseAPI.list(params.page_no, params.page_size, search);
  300. const items = res.data?.data?.items || res.data?.data?.list || [];
  301. // 批量查询当面付状态
  302. if (items.length > 0) {
  303. const eids = items.map((it: any) => it.enterprise_id).filter(Boolean);
  304. if (eids.length > 0) {
  305. try {
  306. const statusRes = await FacetofaceAPI.batchStatus(eids);
  307. const statusMap = statusRes.data?.data || {};
  308. items.forEach((it: any) => {
  309. it.f2f_status = statusMap[it.enterprise_id] || null;
  310. });
  311. } catch { /* ignore */ }
  312. }
  313. }
  314. return {
  315. list: items,
  316. total: res.data?.data?.total || 0,
  317. };
  318. },
  319. });
  320. const dialogVisible = reactive({
  321. title: "",
  322. visible: false,
  323. type: "apply" as "apply" | "update" | "detail",
  324. });
  325. const currentOutBizNo = ref<string>();
  326. const inviteDialogVisible = ref(false);
  327. const inviteData = ref({
  328. pc_invite_url: "",
  329. expire_time: "",
  330. });
  331. const employeeDialogVisible = ref(false);
  332. const employeeDialogEnterpriseId = ref<string>();
  333. const employeeFormRef = ref();
  334. const isEmployeeSubmitting = ref(false);
  335. async function handleToggleTransfer(row: any) {
  336. row._toggling = true;
  337. try {
  338. await EnterpriseAPI.update(row.enterprise_id, { transfer_enabled: !row.transfer_enabled });
  339. row.transfer_enabled = !row.transfer_enabled;
  340. // 同步更新 store 中当前企业的缓存
  341. const current = enterpriseStore.currentEnterprise;
  342. if (current?.enterprise_id === row.enterprise_id) {
  343. enterpriseStore.currentEnterprise = { ...current, transfer_enabled: row.transfer_enabled };
  344. }
  345. ElMessage.success(row.transfer_enabled ? "已开通转账功能" : "已关闭转账功能");
  346. } catch {
  347. ElMessage.error("操作失败");
  348. } finally {
  349. row._toggling = false;
  350. }
  351. }
  352. function handleOpenDialog(type: "apply" | "update" | "detail") {
  353. dialogVisible.type = type;
  354. if (type === "apply") {
  355. dialogVisible.title = "申请入驻";
  356. } else if (type === "update") {
  357. dialogVisible.title = "编辑企业";
  358. } else {
  359. dialogVisible.title = "企业详情";
  360. }
  361. dialogVisible.visible = true;
  362. }
  363. async function handleCloseDialog() {
  364. dialogVisible.visible = false;
  365. }
  366. function handleSubmit() {
  367. formRef.value?.submitForm();
  368. }
  369. function handleFormSuccess() {
  370. dialogVisible.visible = false;
  371. refreshList();
  372. }
  373. const { pageLoading, loadingText, execute: loadingExecute } = useLoadingAction();
  374. // async function handleApplyInvite(data: { identity_type: string; identity?: string; identity_open_id?: string }) {
  375. // const res = await loadingExecute({
  376. // loadingText: "正在申请入驻...",
  377. // action: () => EnterpriseAPI.applyInvite(data),
  378. // });
  379. // if (res) {
  380. // const inviteUrl = res.data?.data?.pc_invite_url;
  381. // if (inviteUrl) {
  382. // window.open(inviteUrl, "_blank");
  383. // }
  384. // refreshList();
  385. // }
  386. // }
  387. async function handleUnsign(enterpriseId: string) {
  388. await loadingExecute({
  389. confirmMessage: "确认进行企业解约?",
  390. confirmTitle: "警告",
  391. confirmType: "warning",
  392. loadingText: "正在解约...",
  393. action: () => EnterpriseAPI.unsign(enterpriseId),
  394. onSuccess: () => {
  395. ElMessage.success("解约申请已提交");
  396. refreshList();
  397. },
  398. });
  399. }
  400. async function handleDelete(enterpriseId: string) {
  401. await loadingExecute({
  402. confirmMessage: "确认进行企业注销?",
  403. confirmTitle: "警告",
  404. confirmType: "warning",
  405. loadingText: "正在注销...",
  406. action: () => EnterpriseAPI.delete(enterpriseId),
  407. onSuccess: () => {
  408. ElMessage.success("注销申请已提交");
  409. refreshList();
  410. },
  411. });
  412. }
  413. function handleGoToEmployee(enterpriseId?: string) {
  414. if (!enterpriseId) {
  415. ElMessage.warning("该企业暂无企业ID");
  416. return;
  417. }
  418. enterpriseStore.setCurrentEnterprise(enterpriseId);
  419. router.push({
  420. path: "/module_payment/employee",
  421. query: { enterprise_id: enterpriseId },
  422. });
  423. }
  424. function handleAddEmployee(enterpriseId?: string) {
  425. if (!enterpriseId) {
  426. ElMessage.warning("该企业暂无企业ID");
  427. return;
  428. }
  429. employeeDialogEnterpriseId.value = enterpriseId;
  430. employeeDialogVisible.value = true;
  431. }
  432. function handleEmployeeDialogClose() {
  433. employeeDialogVisible.value = false;
  434. employeeDialogEnterpriseId.value = undefined;
  435. }
  436. function handleEmployeeFormSuccess() {
  437. employeeDialogVisible.value = false;
  438. employeeDialogEnterpriseId.value = undefined;
  439. ElMessage.success("添加员工成功");
  440. }
  441. function handleEmployeeFormSubmit() {
  442. if (isEmployeeSubmitting.value) return;
  443. isEmployeeSubmitting.value = true;
  444. employeeFormRef.value?.submitForm().finally(() => {
  445. isEmployeeSubmitting.value = false;
  446. });
  447. }
  448. // ---------- 当面付开通 ----------
  449. const f2fDialogVisible = ref(false);
  450. const f2fFormRef = ref<FormInstance>();
  451. const f2fSubmitting = ref(false);
  452. const currentF2fEnterprise = ref<any>(null);
  453. const f2fForm = reactive({
  454. account: "",
  455. mcc_code: "",
  456. contact_name: "",
  457. contact_mobile: "",
  458. contact_email: "",
  459. remark: "",
  460. shop_sign_board_pic_base64: "",
  461. shop_scene_pic_base64: "",
  462. });
  463. const f2fRules = reactive<FormRules>({
  464. account: [{ required: true, message: "请输入商户支付宝账号或pid", trigger: "blur" }],
  465. contact_name: [{ required: true, message: "请输入联系人姓名", trigger: "blur" }],
  466. contact_mobile: [
  467. { required: true, message: "请输入联系人手机号", trigger: "blur" },
  468. { pattern: /^1\d{10}$/, message: "请输入正确的11位手机号", trigger: "blur" },
  469. ],
  470. });
  471. function handleOpenDetail(row: any) {
  472. currentOutBizNo.value = row.enterprise_id;
  473. handleOpenDialog("detail");
  474. }
  475. function handleOpenF2fApply(enterprise: any) {
  476. currentF2fEnterprise.value = enterprise;
  477. Object.assign(f2fForm, {
  478. account: "",
  479. mcc_code: "",
  480. contact_name: "",
  481. contact_mobile: "",
  482. contact_email: "",
  483. remark: "",
  484. shop_sign_board_pic_base64: "",
  485. shop_scene_pic_base64: "",
  486. });
  487. f2fDialogVisible.value = true;
  488. }
  489. function handleCloseF2fDialog() {
  490. f2fDialogVisible.value = false;
  491. f2fFormRef.value?.resetFields();
  492. }
  493. function fileToBase64(file: File): Promise<string> {
  494. return new Promise((resolve, reject) => {
  495. const reader = new FileReader();
  496. reader.onload = () => resolve((reader.result as string).split(",")[1]);
  497. reader.onerror = reject;
  498. reader.readAsDataURL(file);
  499. });
  500. }
  501. async function handleShopSignBoardPicChange(e: Event) {
  502. const input = e.target as HTMLInputElement;
  503. if (input.files?.[0]) {
  504. f2fForm.shop_sign_board_pic_base64 = await fileToBase64(input.files[0]);
  505. }
  506. }
  507. async function handleShopScenePicChange(e: Event) {
  508. const input = e.target as HTMLInputElement;
  509. if (input.files?.[0]) {
  510. f2fForm.shop_scene_pic_base64 = await fileToBase64(input.files[0]);
  511. }
  512. }
  513. async function handleSubmitF2f() {
  514. if (!f2fFormRef.value || !currentF2fEnterprise.value) return;
  515. try {
  516. await f2fFormRef.value.validate();
  517. } catch {
  518. return;
  519. }
  520. f2fSubmitting.value = true;
  521. try {
  522. const submitData: Record<string, any> = {
  523. enterprise_id: currentF2fEnterprise.value.enterprise_id,
  524. account: f2fForm.account,
  525. mcc_code: f2fForm.mcc_code,
  526. contact_name: f2fForm.contact_name,
  527. contact_mobile: f2fForm.contact_mobile,
  528. };
  529. if (f2fForm.contact_email) submitData.contact_email = f2fForm.contact_email;
  530. if (f2fForm.remark) submitData.remark = f2fForm.remark;
  531. if (f2fForm.shop_sign_board_pic_base64) submitData.shop_sign_board_pic_base64 = f2fForm.shop_sign_board_pic_base64;
  532. if (f2fForm.shop_scene_pic_base64) submitData.shop_scene_pic_base64 = f2fForm.shop_scene_pic_base64;
  533. await FacetofaceAPI.apply(submitData as any);
  534. ElMessage.success("当面付代开通申请已提交");
  535. handleCloseF2fDialog();
  536. refreshList();
  537. } catch (e: any) {
  538. ElMessage.error(e?.response?.data?.msg || "提交失败");
  539. } finally {
  540. f2fSubmitting.value = false;
  541. }
  542. }
  543. // ---------- 授权链接 ----------
  544. async function handleGetAuthUrl(row: any) {
  545. try {
  546. const res = await EnterpriseAPI.getAuthUrl(row.enterprise_id);
  547. const authUrl = res.data?.data;
  548. if (authUrl) {
  549. window.location.href = authUrl;
  550. } else {
  551. ElMessage.error("未获取到授权链接");
  552. }
  553. } catch {
  554. ElMessage.error("获取授权链接失败");
  555. }
  556. }
  557. </script>
  558. <style lang="scss" scoped>
  559. .enterprise-empty {
  560. min-height: 60vh;
  561. display: flex;
  562. align-items: center;
  563. justify-content: center;
  564. .empty-content {
  565. text-align: center;
  566. padding: 40px 60px;
  567. background-color: #ffffff;
  568. border-radius: 2px;
  569. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  570. max-width: 400px;
  571. .empty-icon {
  572. margin-bottom: 20px;
  573. .empty-icon-large {
  574. font-size: 64px;
  575. color: #409eff;
  576. }
  577. }
  578. .empty-title {
  579. font-size: 20px;
  580. font-weight: 600;
  581. color: #303133;
  582. margin-bottom: 12px;
  583. }
  584. .empty-desc {
  585. font-size: 14px;
  586. color: #606266;
  587. margin-bottom: 30px;
  588. line-height: 1.5;
  589. }
  590. .el-button {
  591. padding: 10px 30px;
  592. font-size: 16px;
  593. }
  594. }
  595. }
  596. </style>