BasicInfoTab.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. <template>
  2. <div class="tab-content">
  3. <div class="info-section">
  4. <div class="section-title-row">
  5. <div class="section-title-left">
  6. <span class="section-title-text">企业基本信息</span>
  7. <span class="section-title-divider">/</span>
  8. <span class="supplier-no">供应商编码:{{ detailData.supplierNo }}</span>
  9. <el-tag
  10. v-if="detailData?.supplyStatus !== undefined && detailData?.supplyStatus !== null"
  11. :type="getStatusConfig(detailData.supplyStatus).type"
  12. effect="light"
  13. style="margin-left: 8px;"
  14. >
  15. {{ getStatusConfig(detailData.supplyStatus).text }}
  16. </el-tag>
  17. </div>
  18. <div v-if="!isViewMode" class="section-title-actions">
  19. <el-button type="primary" :icon="isEditing ? 'Document' : 'Edit'" @click="onPrimaryAction">
  20. {{ isEditing ? '保存' : '编辑' }}
  21. </el-button>
  22. <el-button v-if="isEditing" @click="onCancelEdit">取消</el-button>
  23. </div>
  24. </div>
  25. <el-form :model="detailData" label-width="120px" class="detail-form">
  26. <el-row :gutter="12" class="form-row">
  27. <el-col :span="8">
  28. <el-form-item label="所属公司:" required>
  29. <el-select v-model="detailData.ownedCompany" placeholder="请选择" clearable filterable style="width: 100%;" :disabled="!isAddMode || !isEditing">
  30. <el-option v-for="company in companyOptions" :key="company.id" :label="company.companyName" :value="company.id" />
  31. </el-select>
  32. </el-form-item>
  33. </el-col>
  34. <el-col :span="8">
  35. <el-form-item label="企业名称:" required>
  36. <el-input v-model="detailData.enterpriseName" placeholder="请输入企业名称" :disabled="!isAddMode || !isEditing" />
  37. </el-form-item>
  38. </el-col>
  39. <el-col :span="8">
  40. <el-form-item label="工商名称:" required>
  41. <el-input
  42. v-model="detailData.businessName"
  43. placeholder="请输入工商名称"
  44. :disabled="!isEditing"
  45. />
  46. </el-form-item>
  47. </el-col>
  48. </el-row>
  49. <el-row :gutter="12" class="form-row">
  50. <el-col :span="8">
  51. <el-form-item label="企业简称:" required>
  52. <el-input v-model="detailData.shortName" placeholder="请输入企业简称" :disabled="!isEditing" />
  53. </el-form-item>
  54. </el-col>
  55. <el-col :span="8">
  56. <el-form-item label="供应商等级:" required>
  57. <el-select v-model="detailData.cooperateLevel" placeholder="请选择" clearable filterable style="width: 100%;" :disabled="!isAddMode || !isEditing">
  58. <el-option v-for="level in supplierLevelOptions" :key="level.id" :label="level.supplierLevelName" :value="String(level.id)" />
  59. </el-select>
  60. </el-form-item>
  61. </el-col>
  62. <el-col :span="8">
  63. <el-form-item label="企业规模:" required>
  64. <el-select v-model="detailData.membershipSize" placeholder="请选择" clearable filterable style="width: 100%;" :disabled="!isEditing">
  65. <el-option v-for="scale in enterpriseScaleOptions" :key="scale.id" :label="scale.enterpriseScaleName" :value="scale.id" />
  66. </el-select>
  67. </el-form-item>
  68. </el-col>
  69. </el-row>
  70. <el-row :gutter="12" class="form-row">
  71. <el-col :span="8">
  72. <el-form-item label="行业类别:" required>
  73. <el-select v-model="detailData.industrCategory" placeholder="请选择" clearable filterable style="width: 100%;" :disabled="!isAddMode || !isEditing">
  74. <el-option v-for="industry in industryCategoryOptions" :key="industry.id" :label="industry.industryCategoryName" :value="industry.id" />
  75. </el-select>
  76. </el-form-item>
  77. </el-col>
  78. <el-col :span="8">
  79. <el-form-item label="供应商类型:" required>
  80. <el-select v-model="detailData.supplierType" placeholder="请选择" clearable filterable style="width: 100%;" :disabled="!isAddMode || !isEditing">
  81. <el-option v-for="type in supplierTypeOptions" :key="type.id" :label="type.supplierTypeName" :value="type.id" />
  82. </el-select>
  83. </el-form-item>
  84. </el-col>
  85. <el-col :span="8">
  86. <el-form-item label="固定电话:">
  87. <el-input
  88. v-model="detailData.fixedPhone"
  89. placeholder="请输入固定电话"
  90. type="tel"
  91. @input="onFixedPhoneInput"
  92. :disabled="!isEditing"
  93. />
  94. </el-form-item>
  95. </el-col>
  96. </el-row>
  97. <el-row :gutter="12" class="form-row">
  98. <el-col :span="8">
  99. <el-form-item label="传真:">
  100. <el-input v-model="detailData.fax" placeholder="请输入传真" :disabled="!isEditing" />
  101. </el-form-item>
  102. </el-col>
  103. <el-col :span="8">
  104. <el-form-item label="企业邮箱:">
  105. <el-input v-model="detailData.mailbox" placeholder="请输入企业邮箱" :disabled="!isEditing" />
  106. </el-form-item>
  107. </el-col>
  108. <el-col :span="8">
  109. <el-form-item label="开始时间:">
  110. <el-date-picker v-model="detailData.validityFromDate" type="date" placeholder="请选择" style="width: 100%;" :disabled="!isAddMode || !isEditing" />
  111. </el-form-item>
  112. </el-col>
  113. </el-row>
  114. <el-row :gutter="12" class="form-row">
  115. <el-col :span="8">
  116. <el-form-item label="结束时间:">
  117. <el-date-picker v-model="detailData.validityToDate" type="date" placeholder="请选择" style="width: 100%;" :disabled="!isAddMode || !isEditing" />
  118. </el-form-item>
  119. </el-col>
  120. <el-col :span="8">
  121. <el-form-item label="邮政编码:">
  122. <el-input v-model="detailData.postCode" placeholder="请输入邮政编码" :disabled="!isEditing" />
  123. </el-form-item>
  124. </el-col>
  125. <el-col :span="8">
  126. <el-form-item label="网址:">
  127. <el-input v-model="detailData.url" placeholder="请输入网址" :disabled="!isEditing" />
  128. </el-form-item>
  129. </el-col>
  130. </el-row>
  131. <el-row :gutter="12" class="form-row">
  132. <el-col :span="8">
  133. <el-form-item label="详细地址:" required>
  134. <el-cascader v-model="selectedOfficeRegionProxy" :options="regionOptions" placeholder="请选择省市区" clearable filterable style="width: 100%;" @change="onOfficeRegionChange" :disabled="!isEditing" />
  135. </el-form-item>
  136. </el-col>
  137. <el-col :span="8">
  138. <el-form-item label=" " label-width="0">
  139. <el-input v-model="detailData.officeAddress" placeholder="请输入详细地址" :disabled="!isEditing" />
  140. </el-form-item>
  141. </el-col>
  142. </el-row>
  143. <el-row :gutter="12" class="form-row">
  144. <el-col :span="8">
  145. <el-form-item label="营业执照:">
  146. <ImageUpload
  147. v-model="detailData.businessLicense"
  148. :limit="1"
  149. :disabled="isViewMode || !isEditing"
  150. :file-size="5"
  151. :file-type="['png', 'jpg', 'jpeg']"
  152. :is-show-tip="false"
  153. />
  154. </el-form-item>
  155. </el-col>
  156. <el-col :span="8">
  157. <el-form-item label="法人身份证照片:">
  158. <ImageUpload
  159. v-model="detailData.personImage"
  160. :limit="1"
  161. :disabled="isViewMode || !isEditing"
  162. :file-size="5"
  163. :file-type="['png', 'jpg', 'jpeg']"
  164. :is-show-tip="false"
  165. />
  166. </el-form-item>
  167. </el-col>
  168. </el-row>
  169. </el-form>
  170. </div>
  171. <div class="info-section">
  172. <div class="section-title">工商信息</div>
  173. <el-row :gutter="12" class="form-row">
  174. <el-col :span="8">
  175. <div class="form-item">
  176. <span class="label">企业工商名称:</span>
  177. <span class="value">{{ businessInfo?.businessName || detailData.businessName || '' }}</span>
  178. </div>
  179. </el-col>
  180. <el-col :span="8">
  181. <div class="form-item">
  182. <span class="label">登记机关:</span>
  183. <span class="value">{{ businessInfo?.registrationAuthority || '' }}</span>
  184. </div>
  185. </el-col>
  186. <el-col :span="8">
  187. <div class="form-item">
  188. <span class="label">成立日期:</span>
  189. <span class="value">{{ formatDate(businessInfo?.establishmentDate) || '' }}</span>
  190. </div>
  191. </el-col>
  192. </el-row>
  193. <el-row :gutter="12" class="form-row">
  194. <el-col :span="8">
  195. <div class="form-item">
  196. <span class="label">登记状态:</span>
  197. <span class="value">{{ businessInfo?.registrationStatus || '' }}</span>
  198. </div>
  199. </el-col>
  200. <el-col :span="8">
  201. <div class="form-item">
  202. <span class="label">实缴资本:</span>
  203. <span class="value">{{ businessInfo?.paidInCapital || '' }}</span>
  204. </div>
  205. </el-col>
  206. <el-col :span="8">
  207. <div class="form-item">
  208. <span class="label">社会信用代码:</span>
  209. <span class="value">{{ businessInfo?.socialCreditCode || detailData.socialCreditCode || '' }}</span>
  210. </div>
  211. </el-col>
  212. </el-row>
  213. <el-row :gutter="12" class="form-row">
  214. <el-col :span="8">
  215. <div class="form-item">
  216. <span class="label">法人姓名:</span>
  217. <span class="value">{{ businessInfo?.legalPersonName || '' }}</span>
  218. </div>
  219. </el-col>
  220. <el-col :span="8">
  221. <div class="form-item">
  222. <span class="label">注册资本:</span>
  223. <span class="value">{{ businessInfo?.registeredCapital || '' }}</span>
  224. </div>
  225. </el-col>
  226. </el-row>
  227. <el-row :gutter="12" class="form-row">
  228. <el-col :span="24">
  229. <div class="form-item">
  230. <span class="label">工商地址:</span>
  231. <el-input v-model="businessInfo.businessAddress" placeholder="工商地址" style="width: 70%;" :disabled="!isEditing" />
  232. </div>
  233. </el-col>
  234. </el-row>
  235. </div>
  236. <div class="info-section">
  237. <div class="section-title-row">
  238. <div class="section-title-left">
  239. <span class="section-title-text">付款信息</span>
  240. </div>
  241. </div>
  242. <el-table :data="paymentInfoList" border style="width: 100%">
  243. <el-table-column prop="isture" label="是否主账号" align="center">
  244. <template #default="scope">
  245. <span>{{ Number(scope.row.isture) === 1 ? '是' : '否' }}</span>
  246. </template>
  247. </el-table-column>
  248. <el-table-column prop="bankName" label="开户银行" align="center" />
  249. <el-table-column prop="bankNo" label="银行账户" align="center" />
  250. </el-table>
  251. </div>
  252. </div>
  253. </template>
  254. <script setup lang="ts">
  255. import ImageUpload from '@/components/ImageUpload/index.vue';
  256. import { scmEditInfo } from '@/api/supplier/info';
  257. import { ref, computed, watch } from 'vue';
  258. import { useRoute } from 'vue-router';
  259. const props = defineProps<{
  260. detailData: any;
  261. companyOptions: any[];
  262. enterpriseScaleOptions: any[];
  263. industryCategoryOptions: any[];
  264. supplierLevelOptions: any[];
  265. supplierTypeOptions: any[];
  266. isAddMode: boolean;
  267. isViewMode: boolean;
  268. isBasicInfoSaved: boolean;
  269. businessInfo?: any;
  270. businessInfoLoading?: boolean;
  271. regionOptions: any[];
  272. selectedOfficeRegion: string[];
  273. paymentInfoList: any[];
  274. formatDate: (date: any) => string;
  275. }>();
  276. const emit = defineEmits<{
  277. (e: 'save'): void;
  278. (e: 'officeRegionChange', value: string[]): void;
  279. (e: 'update:selectedOfficeRegion', value: string[]): void;
  280. (e: 'addPayment'): void;
  281. (e: 'viewPayment', row: any): void;
  282. (e: 'editPayment', row: any): void;
  283. }>();
  284. const selectedOfficeRegionProxy = computed({
  285. get: () => props.selectedOfficeRegion,
  286. set: (value) => emit('update:selectedOfficeRegion', value)
  287. });
  288. const onOfficeRegionChange = (val: unknown) => {
  289. emit('officeRegionChange', (val || []) as string[]);
  290. };
  291. const onFixedPhoneInput = (val: string) => {
  292. const next = (val || '').replace(/\D+/g, '');
  293. if (next !== props.detailData.fixedPhone) {
  294. props.detailData.fixedPhone = next;
  295. }
  296. };
  297. const safeFormatDate = (date: any) => {
  298. return typeof props.formatDate === 'function' ? props.formatDate(date) : '';
  299. };
  300. const isEditing = ref(false);
  301. const getStatusConfig = (status: string | number) => {
  302. const s = String(status);
  303. const map: Record<string, { text: string; type: 'success' | 'info' | 'warning' | 'primary' | 'danger' }> = {
  304. '0': { text: '待审核', type: 'warning' },
  305. '1': { text: '生效', type: 'success' },
  306. '2': { text: '停止合作', type: 'danger' },
  307. '3': { text: '审核不通过', type: 'danger' },
  308. '4': { text: '待修改审核', type: 'warning' }
  309. };
  310. return map[s] || { text: '未知状态', type: 'info' };
  311. };
  312. const route = useRoute();
  313. const saveLoading = ref(false);
  314. const backupDetailData = ref<any>(null);
  315. const backupOfficeRegion = ref<string[] | null>(null);
  316. const backupBusinessAddress = ref<string | null>(null);
  317. const originDetailData = ref<any>(null);
  318. const makeSnapshot = (data: any) => {
  319. try {
  320. return JSON.parse(JSON.stringify(data ?? {}));
  321. } catch (e) {
  322. return data;
  323. }
  324. };
  325. const getChangedFields = (origin: any, current: any) => {
  326. const o = origin ?? {};
  327. const c = current ?? {};
  328. const changed: any = {};
  329. Object.keys(c).forEach((k) => {
  330. const ov = (o as any)[k];
  331. const cv = (c as any)[k];
  332. try {
  333. if (JSON.stringify(ov) !== JSON.stringify(cv)) {
  334. changed[k] = cv;
  335. }
  336. } catch (e) {
  337. if (ov !== cv) {
  338. changed[k] = cv;
  339. }
  340. }
  341. });
  342. return changed;
  343. };
  344. watch(
  345. () => (props.detailData as any)?.id,
  346. () => {
  347. originDetailData.value = makeSnapshot(props.detailData);
  348. },
  349. { immediate: true }
  350. );
  351. const applySnapshot = (target: any, snapshot: any) => {
  352. if (!target || !snapshot || typeof target !== 'object') return;
  353. Object.keys(target).forEach((k) => {
  354. delete target[k];
  355. });
  356. Object.assign(target, makeSnapshot(snapshot));
  357. };
  358. const onPrimaryAction = () => {
  359. if (!isEditing.value) {
  360. backupDetailData.value = makeSnapshot(props.detailData);
  361. backupOfficeRegion.value = makeSnapshot(props.selectedOfficeRegion);
  362. backupBusinessAddress.value = props.businessInfo?.businessAddress ?? null;
  363. isEditing.value = true;
  364. return;
  365. }
  366. if (saveLoading.value) return;
  367. saveLoading.value = true;
  368. const id = (route.query.id as string) || (props.detailData as any)?.id;
  369. const changed = getChangedFields(originDetailData.value, props.detailData);
  370. scmEditInfo({
  371. ...changed,
  372. id
  373. } as any)
  374. .then(() => {
  375. originDetailData.value = makeSnapshot(props.detailData);
  376. emit('save');
  377. isEditing.value = false;
  378. })
  379. .finally(() => {
  380. saveLoading.value = false;
  381. });
  382. };
  383. const onCancelEdit = () => {
  384. if (!isEditing.value) return;
  385. applySnapshot(props.detailData, backupDetailData.value);
  386. if (backupOfficeRegion.value) {
  387. emit('update:selectedOfficeRegion', makeSnapshot(backupOfficeRegion.value));
  388. emit('officeRegionChange', makeSnapshot(backupOfficeRegion.value));
  389. }
  390. if (props.businessInfo && backupBusinessAddress.value !== null) {
  391. props.businessInfo.businessAddress = backupBusinessAddress.value;
  392. }
  393. isEditing.value = false;
  394. };
  395. </script>
  396. <style scoped>
  397. .tab-content {
  398. --el-component-size: 24px;
  399. padding: 16px;
  400. }
  401. .detail-form :deep(.el-form-item__label) {
  402. white-space: nowrap;
  403. }
  404. .detail-form :deep(.form-row) {
  405. margin-bottom: 2px;
  406. }
  407. .detail-form :deep(.el-form-item) {
  408. margin-bottom: 8px;
  409. }
  410. .tab-content :deep(.form-row) {
  411. margin-bottom: 2px;
  412. }
  413. .tab-content :deep(.el-form-item) {
  414. margin-bottom: 8px;
  415. }
  416. .tab-content :deep(.info-section) {
  417. margin-bottom: 20px;
  418. }
  419. .tab-content :deep(.section-title),
  420. .tab-content :deep(.section-title-row) {
  421. margin-bottom: 12px;
  422. padding-bottom: 8px;
  423. }
  424. .tab-content :deep(.section-title-text) {
  425. font-size: 14px;
  426. }
  427. .tab-content :deep(.section-title-actions) {
  428. display: flex;
  429. gap: 8px;
  430. }
  431. .tab-content :deep(.supplier-no) {
  432. font-size: 13px;
  433. }
  434. .tab-content :deep(.el-form-item__label) {
  435. font-size: 13px;
  436. height: var(--el-component-size);
  437. line-height: var(--el-component-size);
  438. padding-top: 0;
  439. padding-bottom: 0;
  440. }
  441. .tab-content :deep(.form-item) {
  442. line-height: 24px;
  443. font-size: 13px;
  444. }
  445. .detail-form :deep(.el-input),
  446. .detail-form :deep(.el-select),
  447. .detail-form :deep(.el-date-editor),
  448. .detail-form :deep(.el-cascader) {
  449. width: 100%;
  450. }
  451. .detail-form :deep(.el-input__wrapper),
  452. .detail-form :deep(.el-select__wrapper),
  453. .detail-form :deep(.el-date-editor),
  454. .detail-form :deep(.el-cascader) {
  455. min-height: var(--el-component-size);
  456. }
  457. .detail-form :deep(.el-input__inner) {
  458. height: var(--el-component-size);
  459. line-height: var(--el-component-size);
  460. }
  461. .detail-form :deep(.el-form-item__content) {
  462. align-items: center;
  463. min-height: var(--el-component-size);
  464. }
  465. .detail-form :deep(.el-input__wrapper),
  466. .detail-form :deep(.el-select__wrapper),
  467. .detail-form :deep(.el-date-editor),
  468. .detail-form :deep(.el-cascader) {
  469. height: var(--el-component-size);
  470. align-items: center;
  471. padding-top: 0;
  472. padding-bottom: 0;
  473. }
  474. .detail-form :deep(.el-select__selected-item),
  475. .detail-form :deep(.el-select__input),
  476. .detail-form :deep(.el-range-input) {
  477. line-height: var(--el-component-size);
  478. }
  479. :deep(.component-upload-image .el-upload--picture-card),
  480. :deep(.component-upload-image .el-upload-list--picture-card .el-upload-list__item) {
  481. width: 64px;
  482. height: 64px;
  483. }
  484. :deep(.component-upload-image .el-upload-list--picture-card .el-upload-list__item-thumbnail) {
  485. width: 64px;
  486. height: 64px;
  487. }
  488. </style>