sales-partners-modal.vue 12 KB

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