index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <template>
  2. <div class="app-container h-full flex flex-1 flex-col">
  3. <!-- 搜索 -->
  4. <page-search
  5. ref="searchRef"
  6. :search-config="searchConfig"
  7. @query-click="handleQueryClick"
  8. @reset-click="handleResetClick"
  9. >
  10. <template #status="scope">
  11. <Dict v-model="scope.formData[scope.prop]" code="status" v-bind="scope.attrs" />
  12. </template>
  13. </page-search>
  14. <!-- 列表 -->
  15. <page-content
  16. ref="contentRef"
  17. :content-config="contentConfig"
  18. @add-click="handleAddClick"
  19. @export-click="handleExportClick"
  20. @search-click="handleSearchClick"
  21. @toolbar-click="handleToolbarClick"
  22. @operate-click="handleOperateClick"
  23. @filter-change="handleFilterChange"
  24. >
  25. <template #status="scope">
  26. <DictLabel v-model="scope.row[scope.prop]" code="status" />
  27. </template>
  28. <template #ecAttach="scope">
  29. <div v-if="scope.row[scope.prop]">
  30. <el-image
  31. v-if="isImageFile(scope.row[scope.prop])"
  32. :src="scope.row[scope.prop]"
  33. :preview-src-list="[scope.row[scope.prop]]"
  34. preview-teleported
  35. style="width: 50px; height: 50px; cursor: pointer;"
  36. fit="cover"
  37. lazy
  38. />
  39. <el-link
  40. v-else
  41. :href="scope.row[scope.prop]"
  42. target="_blank"
  43. type="primary"
  44. :underline="false"
  45. >
  46. {{ getFileName(scope.row[scope.prop]) }}
  47. </el-link>
  48. </div>
  49. <span v-else>无</span>
  50. </template>
  51. </page-content>
  52. <!-- 新增 -->
  53. <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
  54. <template #status="scope">
  55. <Dict v-model="scope.formData[scope.prop]" code="status" v-bind="scope.attrs" />
  56. </template>
  57. <template #ecAttach="scope">
  58. <FileUpload
  59. v-model="fileList[scope.prop]"
  60. :limit="1"
  61. @update:model-value="handleFileUploadChange($event, scope.formData, scope.prop)"
  62. />
  63. </template>
  64. </page-modal>
  65. <!-- 编辑 -->
  66. <page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
  67. <template #status="scope">
  68. <Dict v-model="scope.formData[scope.prop]" code="status" v-bind="scope.attrs" />
  69. </template>
  70. <template #ecAttach="scope">
  71. <FileUpload
  72. v-model="fileList[scope.prop]"
  73. :limit="1"
  74. @update:model-value="handleFileUploadChange($event, scope.formData, scope.prop)"
  75. />
  76. </template>
  77. </page-modal>
  78. </div>
  79. </template>
  80. <script setup lang="ts">
  81. defineOptions({ name: "ThirdPartyInfo" });
  82. import ThirdPartyInfoAPI, { ThirdPartyInfoForm, ThirdPartyInfoPageQuery } from "@/api/operationsManage/third-party-info-api";
  83. import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
  84. import usePage from "@/components/CURD/usePage";
  85. import FileUpload from "@/components/Upload/FileUpload.vue";
  86. import { ref } from "vue";
  87. import type { FileInfo } from "@/api/file-api";
  88. // 组合式 CRUD
  89. const {
  90. searchRef,
  91. contentRef,
  92. addModalRef,
  93. editModalRef,
  94. handleQueryClick,
  95. handleResetClick,
  96. handleAddClick,
  97. handleEditClick,
  98. handleSubmitClick,
  99. handleExportClick,
  100. handleSearchClick,
  101. handleFilterChange,
  102. } = usePage();
  103. // 文件列表
  104. const fileList = ref<Record<string, FileInfo[]>>({});
  105. // 处理文件上传变化
  106. const handleFileUploadChange = (files: any[], formData: any, prop: string) => {
  107. if (files && files.length > 0) {
  108. // 取第一个文件的URL作为值
  109. formData[prop] = files[0].url;
  110. } else {
  111. formData[prop] = undefined;
  112. }
  113. };
  114. // 判断是否为图片文件
  115. const isImageFile = (url: string) => {
  116. if (!url) return false;
  117. const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
  118. const ext = url.split('.').pop()?.toLowerCase() || '';
  119. return imageExtensions.includes(ext);
  120. };
  121. // 获取文件名
  122. const getFileName = (url: string) => {
  123. if (!url) return '';
  124. const fileName = url.split('/').pop() || '';
  125. return fileName || '下载文件';
  126. };
  127. // 搜索配置
  128. const searchConfig: ISearchConfig = reactive({
  129. permPrefix: "system:third-party-info",
  130. formItems: [
  131. {
  132. type: "input",
  133. label: "对接商名称",
  134. prop: "ecName",
  135. attrs: {
  136. placeholder: "对接商名称",
  137. clearable: true,
  138. style: { width: "200px" },
  139. },
  140. },
  141. {
  142. type: "custom",
  143. slotName: "status",
  144. label: "状态", //0 正常 1 停用
  145. prop: "status",
  146. attrs: {
  147. placeholder: "状态",
  148. clearable: true,
  149. style: { width: "200px" },
  150. },
  151. },
  152. ],
  153. });
  154. // 列表配置
  155. const contentConfig: IContentConfig<ThirdPartyInfoPageQuery> = reactive({
  156. // 权限前缀
  157. permPrefix: "system:third-party-info",
  158. table: {
  159. border: true,
  160. highlightCurrentRow: true,
  161. },
  162. // 主键
  163. pk: "id",
  164. // 列表查询接口
  165. indexAction: ThirdPartyInfoAPI.getPage,
  166. // 删除接口
  167. deleteAction: ThirdPartyInfoAPI.deleteByIds,
  168. // 数据解析函数
  169. parseData(res: any) {
  170. return {
  171. total: res.total,
  172. list: res.list,
  173. };
  174. },
  175. // 分页配置
  176. pagination: {
  177. background: true,
  178. layout: "total, sizes, prev, pager, next, jumper",
  179. pageSize: 20,
  180. pageSizes: [10, 20, 30, 50],
  181. },
  182. // 工具栏配置
  183. toolbar: ["add", "delete"],
  184. defaultToolbar: ["refresh", "filter"],
  185. // 表格列配置
  186. cols: [
  187. { type: "selection", width: 55, align: "center" },
  188. { label: "对接商名称", prop: "ecName" },
  189. { label: "联系人", prop: "contactName" },
  190. { label: "联系人电话", prop: "contactPhone" },
  191. {
  192. label: "协议附件",
  193. prop: "ecAttach",
  194. templet: "custom",
  195. slotName: "ecAttach"
  196. },
  197. { label: "对接编号", prop: "appId" },
  198. { label: "授权码", prop: "authCode" },
  199. {
  200. label: "状态", //0 正常 1 停用
  201. prop: "status",
  202. templet: "custom",
  203. slotName: "status"
  204. },
  205. { label: "备注", prop: "remark" },
  206. { label: "创建时间", prop: "createTime" },
  207. {
  208. label: "操作",
  209. prop: "operation",
  210. width: 220,
  211. templet: "tool",
  212. operat: ["edit", "delete"],
  213. },
  214. ],
  215. });
  216. // 新增配置
  217. const addModalConfig: IModalConfig<ThirdPartyInfoForm> = reactive({
  218. // 权限前缀
  219. permPrefix: "system:third-party-info",
  220. // 主键
  221. pk: "id",
  222. // 弹窗配置
  223. dialog: {
  224. title: "新增",
  225. width: 800,
  226. draggable: true,
  227. },
  228. form: {
  229. labelWidth: 100,
  230. },
  231. // 表单项配置
  232. formItems: [
  233. {
  234. type: "input",
  235. attrs: {
  236. placeholder: "对接商名称"
  237. },
  238. label: "对接商名称",
  239. prop: "ecName",
  240. },
  241. {
  242. type: "input",
  243. attrs: {
  244. placeholder: "联系人"
  245. },
  246. label: "联系人",
  247. prop: "contactName",
  248. },
  249. {
  250. type: "input",
  251. attrs: {
  252. placeholder: "联系人电话"
  253. },
  254. label: "联系人电话",
  255. prop: "contactPhone",
  256. },
  257. {
  258. type: "custom",
  259. label: "协议附件",
  260. prop: "ecAttach",
  261. slotName: "ecAttach",
  262. },
  263. {
  264. type: "input",
  265. attrs: {
  266. placeholder: "对接编号"
  267. },
  268. label: "对接编号",
  269. prop: "appId",
  270. },
  271. {
  272. type: "input",
  273. attrs: {
  274. placeholder: "授权码"
  275. },
  276. label: "授权码",
  277. prop: "authCode",
  278. },
  279. {
  280. type: "custom",
  281. label: "状态", //0 正常 1 停用
  282. prop: "status",
  283. slotName: "status",
  284. attrs: {
  285. placeholder: "状态",
  286. style: { width: "100%" }
  287. },
  288. },
  289. {
  290. type: "input",
  291. attrs: {
  292. placeholder: "备注"
  293. },
  294. label: "备注",
  295. prop: "remark",
  296. },
  297. ],
  298. // 提交函数
  299. formAction: (data: ThirdPartyInfoForm) => {
  300. if (data.id) {
  301. // 编辑
  302. return ThirdPartyInfoAPI.update(data.id as string, data);
  303. } else {
  304. // 新增
  305. return ThirdPartyInfoAPI.create(data);
  306. }
  307. },
  308. });
  309. // 编辑配置
  310. const editModalConfig: IModalConfig<ThirdPartyInfoForm> = reactive({
  311. permPrefix: "system:third-party-info",
  312. component: "drawer",
  313. drawer: {
  314. title: "编辑",
  315. size: 500,
  316. },
  317. pk: "id",
  318. formAction(data: any) {
  319. return ThirdPartyInfoAPI.update(data.id as string, data);
  320. },
  321. formItems: addModalConfig.formItems, // 复用新增的表单项
  322. });
  323. // 处理操作按钮点击
  324. const handleOperateClick = (data: IObject) => {
  325. if (data.name === "edit") {
  326. handleEditClick(data.row, async () => {
  327. return await ThirdPartyInfoAPI.getFormData(data.row.id);
  328. });
  329. }
  330. };
  331. // 处理工具栏按钮点击(删除等)
  332. const handleToolbarClick = (name: string) => {
  333. console.log(name);
  334. };
  335. </script>