sales-partners-modal.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <script setup lang="ts">
  2. import { computed, ref, watch } from 'vue';
  3. import { $t } from '@/locales';
  4. import {
  5. Button,
  6. DatePicker,
  7. Input,
  8. message,
  9. Modal,
  10. Switch,
  11. Upload,
  12. } from 'antdv-next';
  13. import MD5 from 'crypto-js/md5';
  14. import dayjs from 'dayjs';
  15. import { createPartnerApi, getLangByKeyApi, updatePartnerApi } from '#/api';
  16. interface Props {
  17. open: boolean;
  18. mode: 'add' | 'edit';
  19. partnerData?: any;
  20. }
  21. const props = defineProps<Props>();
  22. const emit = defineEmits<{
  23. (e: 'save', data: any): void;
  24. (e: 'update:open', value: boolean): void;
  25. }>();
  26. const token = localStorage.getItem('token_a');
  27. const formData = ref({
  28. id: '',
  29. logo: null,
  30. fileId: '',
  31. imgPhotoFileId: '',
  32. fileList: [] as any[],
  33. account: '',
  34. password: '',
  35. nameCn: '',
  36. nameEn: '',
  37. langName: '',
  38. isEnabled: true,
  39. expiredTime: null as any,
  40. number0fEnterprise: 0,
  41. number0fWorkFlow: 0,
  42. number0fPages: 0,
  43. number0fTables: 0,
  44. number0fDesigners: 0,
  45. number0fBusinessScenarios: 0,
  46. number0fCSiteMaxUser: 0,
  47. });
  48. const isOpen = computed({
  49. get: () => props.open,
  50. set: (value) => emit('update:open', value),
  51. });
  52. watch(
  53. () => props.open,
  54. (newValue) => {
  55. if (newValue && props.partnerData) {
  56. fetchPartnerDetail();
  57. } else if (newValue) {
  58. resetForm();
  59. }
  60. },
  61. );
  62. async function fetchPartnerDetail() {
  63. if (!props.partnerData) {
  64. return;
  65. }
  66. formData.value.id = props.partnerData.id || '';
  67. formData.value.account = props.partnerData.account || '';
  68. formData.value.langName = props.partnerData.langName || '';
  69. formData.value.isEnabled = props.partnerData.status ?? true;
  70. formData.value.expiredTime = props.partnerData.expiredTime
  71. ? dayjs(props.partnerData.expiredTime)
  72. : null;
  73. formData.value.number0fEnterprise = props.partnerData.number0fEnterprise || 0;
  74. formData.value.number0fWorkFlow = props.partnerData.number0fWorkFlow || 0;
  75. formData.value.number0fPages = props.partnerData.number0fPages || 0;
  76. formData.value.number0fTables = props.partnerData.number0fTables || 0;
  77. formData.value.number0fDesigners = props.partnerData.number0fDesigners || 0;
  78. formData.value.number0fBusinessScenarios =
  79. props.partnerData.number0fBusinessScenarios || 0;
  80. formData.value.number0fCSiteMaxUser =
  81. props.partnerData.number0fCSiteMaxUser || 0;
  82. if (props.partnerData.imgPhotoFileId) {
  83. formData.value.imgPhotoFileId = props.partnerData.imgPhotoFileId;
  84. formData.value.fileList = [
  85. {
  86. uid: '-1',
  87. name: 'logo.png',
  88. status: 'done',
  89. url: `/File/Download?fileId=${props.partnerData.imgPhotoFileId}`,
  90. },
  91. ];
  92. }
  93. if (props.mode === 'edit' && props.partnerData.langName) {
  94. try {
  95. const result = await getLangByKeyApi(props.partnerData.langName);
  96. if (result?.result) {
  97. formData.value.nameCn = result.result['zh-CN'] || '';
  98. formData.value.nameEn = result.result.en || '';
  99. }
  100. } catch {}
  101. } else {
  102. formData.value.nameCn =
  103. props.partnerData.langNameList?.find((item: any) => item.name === 'zh-CN')
  104. ?.value || '';
  105. formData.value.nameEn =
  106. props.partnerData.langNameList?.find((item: any) => item.name === 'en')
  107. ?.value || '';
  108. }
  109. }
  110. function resetForm() {
  111. formData.value = {
  112. id: '',
  113. logo: null,
  114. fileId: '',
  115. imgPhotoFileId: '',
  116. fileList: [] as any[],
  117. account: '',
  118. password: '',
  119. nameCn: '',
  120. nameEn: '',
  121. langName: '',
  122. isEnabled: true,
  123. expiredTime: null,
  124. number0fEnterprise: 0,
  125. number0fWorkFlow: 0,
  126. number0fPages: 0,
  127. number0fTables: 0,
  128. number0fDesigners: 0,
  129. number0fBusinessScenarios: 0,
  130. number0fCSiteMaxUser: 0,
  131. };
  132. }
  133. function handleLogoUpload(info: any) {
  134. if (info.file.status === 'done') {
  135. formData.value.logo = info.file;
  136. if (info.file.response?.result?.[0]?.id) {
  137. formData.value.fileId = info.file.response.result[0].id;
  138. formData.value.imgPhotoFileId = info.file.response.result[0].id;
  139. }
  140. }
  141. }
  142. async function handleSave() {
  143. const data = {
  144. id: formData.value.id,
  145. langNameList: [
  146. {
  147. name: 'zh-CN',
  148. value: formData.value.nameCn,
  149. },
  150. {
  151. name: 'en',
  152. value: formData.value.nameEn,
  153. },
  154. ],
  155. fileId: formData.value.fileId,
  156. imgPhotoFileId: formData.value.imgPhotoFileId,
  157. version: 'v1',
  158. account: formData.value.account,
  159. langName:
  160. props.mode === 'edit' ? formData.value.langName : formData.value.nameCn,
  161. password:
  162. props.mode === 'add' ? MD5(formData.value.password).toString() : '',
  163. expiredTime: formData.value.expiredTime
  164. ? formData.value.expiredTime.format('YYYY-MM-DD')
  165. : '',
  166. number0fEnterprise: formData.value.number0fEnterprise,
  167. number0fPages: formData.value.number0fPages,
  168. number0fDesigners: formData.value.number0fDesigners,
  169. number0fCSiteMaxUser: formData.value.number0fCSiteMaxUser,
  170. number0fBusinessScenarios: formData.value.number0fBusinessScenarios,
  171. number0fTables: formData.value.number0fTables,
  172. number0fWorkFlow: formData.value.number0fWorkFlow,
  173. status: formData.value.isEnabled,
  174. };
  175. try {
  176. const result =
  177. props.mode === 'add'
  178. ? await createPartnerApi(data)
  179. : await updatePartnerApi(data);
  180. if (result?.isSuccess) {
  181. message.success($t('salesPartners.saveSuccess'));
  182. emit('save', data);
  183. }
  184. } catch (error) {
  185. console.error('保存失败:', error);
  186. }
  187. }
  188. function handleCancel() {
  189. isOpen.value = false;
  190. }
  191. </script>
  192. <template>
  193. <Modal
  194. v-model:open="isOpen"
  195. :footer="null"
  196. :title="
  197. mode === 'add'
  198. ? $t('salesPartners.modal.addTitle')
  199. : $t('salesPartners.modal.editTitle')
  200. "
  201. class="mt-[-50px]"
  202. width="1200px"
  203. >
  204. <div class="">
  205. <div class="flex-1 overflow-y-auto p-6">
  206. <div class="space-y-4">
  207. <div class="flex items-center gap-4">
  208. <div class="flex flex-col gap-2">
  209. <label class="text-sm font-medium">{{
  210. $t('salesPartners.modal.enterpriseLogo')
  211. }}</label>
  212. <Upload
  213. v-model:file-list="formData.fileList"
  214. :headers="{ Authorization: String(token) }"
  215. :max-count="1"
  216. action="/fileApi/File/UploadFiles"
  217. list-type="picture-card"
  218. @change="handleLogoUpload"
  219. >
  220. <div
  221. class="flex h-[100px] w-[200px] items-center justify-center border-2 border-dashed"
  222. >
  223. <div class="text-center">
  224. <div class="text-4xl">+</div>
  225. <div class="text-sm text-gray-500">
  226. {{ $t('salesPartners.modal.uploadLogo') }}
  227. </div>
  228. </div>
  229. </div>
  230. </Upload>
  231. </div>
  232. </div>
  233. <div class="grid grid-cols-2 gap-4">
  234. <div class="flex flex-col gap-2">
  235. <label class="text-sm font-medium">{{
  236. $t('salesPartners.modal.account')
  237. }}</label>
  238. <Input
  239. v-model:value="formData.account"
  240. :placeholder="$t('salesPartners.modal.enterAccount')"
  241. />
  242. </div>
  243. <div v-if="mode === 'add'" class="flex flex-col gap-2">
  244. <label class="text-sm font-medium">{{
  245. $t('salesPartners.modal.password')
  246. }}</label>
  247. <Input
  248. v-model:value="formData.password"
  249. :placeholder="$t('salesPartners.modal.enterPassword')"
  250. />
  251. </div>
  252. <div class="flex flex-col gap-2">
  253. <label class="text-sm font-medium">{{
  254. $t('salesPartners.modal.nameCn')
  255. }}</label>
  256. <Input
  257. v-model:value="formData.nameCn"
  258. :placeholder="$t('salesPartners.modal.enterName')"
  259. />
  260. </div>
  261. <div class="flex flex-col gap-2">
  262. <label class="text-sm font-medium">{{
  263. $t('salesPartners.modal.nameEn')
  264. }}</label>
  265. <Input
  266. v-model:value="formData.nameEn"
  267. :placeholder="$t('salesPartners.modal.enterName')"
  268. />
  269. </div>
  270. <div class="flex flex-col gap-2">
  271. <label class="text-sm font-medium">{{
  272. $t('salesPartners.modal.expiredTime')
  273. }}</label>
  274. <DatePicker
  275. v-model:value="formData.expiredTime"
  276. :placeholder="$t('salesPartners.modal.selectExpiredTime')"
  277. class="h-[32px] w-full"
  278. format="YYYY-MM-DD"
  279. />
  280. </div>
  281. <div class="flex flex-col gap-2">
  282. <label class="text-sm font-medium">{{
  283. $t('salesPartners.modal.isEnabled')
  284. }}</label>
  285. <Switch v-model:checked="formData.isEnabled" class="w-[40px]" />
  286. </div>
  287. <div class="flex flex-col gap-2">
  288. <label class="text-sm font-medium">{{
  289. $t('salesPartners.modal.number0fEnterprise')
  290. }}</label>
  291. <Input
  292. v-model:value="formData.number0fEnterprise"
  293. type="number"
  294. />
  295. </div>
  296. <div class="flex flex-col gap-2">
  297. <label class="text-sm font-medium">{{
  298. $t('salesPartners.modal.number0fWorkFlow')
  299. }}</label>
  300. <Input v-model:value="formData.number0fWorkFlow" type="number" />
  301. </div>
  302. <div class="flex flex-col gap-2">
  303. <label class="text-sm font-medium">{{
  304. $t('salesPartners.modal.number0fPages')
  305. }}</label>
  306. <Input v-model:value="formData.number0fPages" type="number" />
  307. </div>
  308. <div class="flex flex-col gap-2">
  309. <label class="text-sm font-medium">{{
  310. $t('salesPartners.modal.number0fTables')
  311. }}</label>
  312. <Input v-model:value="formData.number0fTables" type="number" />
  313. </div>
  314. <div class="flex flex-col gap-2">
  315. <label class="text-sm font-medium">{{
  316. $t('salesPartners.modal.number0fDesigners')
  317. }}</label>
  318. <Input v-model:value="formData.number0fDesigners" type="number" />
  319. </div>
  320. <div class="flex flex-col gap-2">
  321. <label class="text-sm font-medium">{{
  322. $t('salesPartners.modal.number0fBusinessScenarios')
  323. }}</label>
  324. <Input
  325. v-model:value="formData.number0fBusinessScenarios"
  326. type="number"
  327. />
  328. </div>
  329. <div class="flex flex-col gap-2">
  330. <label class="text-sm font-medium">{{
  331. $t('salesPartners.modal.number0fCSiteMaxUser')
  332. }}</label>
  333. <Input
  334. v-model:value="formData.number0fCSiteMaxUser"
  335. type="number"
  336. />
  337. </div>
  338. </div>
  339. </div>
  340. </div>
  341. <div class="flex justify-end gap-4">
  342. <Button @click="handleCancel">
  343. {{ $t('salesPartners.modal.cancel') }}
  344. </Button>
  345. <Button type="primary" @click="handleSave">
  346. {{ $t('salesPartners.modal.save') }}
  347. </Button>
  348. </div>
  349. </div>
  350. </Modal>
  351. </template>
  352. <style lang="scss" scoped>
  353. :deep(.ant-upload-list-picture-card-container) {
  354. .ant-upload-list-item {
  355. width: 80px;
  356. height: 80px;
  357. }
  358. }
  359. </style>