index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. <template>
  2. <div class="p-2">
  3. <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
  4. <div v-show="showSearch" class="mb-[10px]">
  5. <el-card shadow="hover">
  6. <el-form ref="queryFormRef" :model="queryParams" :inline="true">
  7. <el-form-item label="版本名称" prop="versionName">
  8. <el-input v-model="queryParams.versionName" placeholder="请输入版本名称" clearable @keyup.enter="handleQuery" />
  9. </el-form-item>
  10. <el-form-item label="发布日期" prop="releaseDate">
  11. <el-date-picker clearable v-model="queryParams.releaseDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择发布日期" />
  12. </el-form-item>
  13. <el-form-item label="下载链接" prop="downloadLink">
  14. <el-input v-model="queryParams.downloadLink" placeholder="请输入下载链接" clearable @keyup.enter="handleQuery" />
  15. </el-form-item>
  16. <el-form-item>
  17. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  18. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  19. </el-form-item>
  20. </el-form>
  21. </el-card>
  22. </div>
  23. </transition>
  24. <el-card shadow="never">
  25. <template #header>
  26. <el-row :gutter="10" class="mb8">
  27. <el-col :span="1.5">
  28. <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['business:versionManagement:add']">新增</el-button>
  29. </el-col>
  30. <el-col :span="1.5">
  31. <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['business:versionManagement:edit']"
  32. >修改</el-button
  33. >
  34. </el-col>
  35. <el-col :span="1.5">
  36. <el-button
  37. type="danger"
  38. plain
  39. icon="Delete"
  40. :disabled="multiple"
  41. @click="handleDelete()"
  42. v-hasPermi="['business:versionManagement:remove']"
  43. >删除</el-button
  44. >
  45. </el-col>
  46. <!-- <el-col :span="1.5">
  47. <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['business:versionManagement:export']">导出</el-button>
  48. </el-col>-->
  49. <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
  50. </el-row>
  51. </template>
  52. <el-table v-loading="loading" border :data="versionManagementList" @selection-change="handleSelectionChange">
  53. <el-table-column type="selection" width="55" align="center" />
  54. <el-table-column label="编号" align="center" prop="id" v-if="true" />
  55. <el-table-column label="版本号" align="center" prop="versionCode" />
  56. <el-table-column label="版本名称" align="center" prop="versionName" />
  57. <el-table-column label="下载链接" align="center" prop="downloadLink" />
  58. <el-table-column label="主要更新内容" align="center" width="180">
  59. <template #default="scope">
  60. <el-tooltip :content="scope.row.updateContent" placement="top">
  61. <span class="truncate">{{ scope.row.updateContent }}</span>
  62. </el-tooltip>
  63. </template>
  64. </el-table-column>
  65. <!-- <el-table-column label="已知问题" align="center" width="180">
  66. <template #default="scope">
  67. <el-tooltip :content="scope.row.knownIssues" placement="top">
  68. <span class="truncate">{{ scope.row.knownIssues }}</span>
  69. </el-tooltip>
  70. </template>
  71. </el-table-column>-->
  72. <el-table-column label="操作系统类型" align="center" prop="osType" />
  73. <!-- <el-table-column label="系统版本" align="center" prop="osSupport" />-->
  74. <el-table-column label="是否强制更新" align="center">
  75. <template #default="scope">
  76. {{ formatForceUpdate(scope.row.forceUpdate) }}
  77. </template>
  78. </el-table-column>
  79. <!-- <el-table-column label="更新包大小" align="center" prop="updatePackageSize" />-->
  80. <el-table-column label="状态" align="center">
  81. <template #default="scope">
  82. {{ formatStatus(scope.row.status) }}
  83. </template>
  84. </el-table-column>
  85. <el-table-column label="发布日期" align="center" prop="releaseDate" width="180">
  86. <template #default="scope">
  87. <span>{{ parseTime(scope.row.releaseDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
  88. </template>
  89. </el-table-column>
  90. <el-table-column label="创建时间" align="center" prop="createdAt" width="180">
  91. <template #default="scope">
  92. <span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
  93. </template>
  94. </el-table-column>
  95. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  96. <template #default="scope">
  97. <el-tooltip content="修改" placement="top">
  98. <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['business:versionManagement:edit']"
  99. >修改</el-button
  100. >
  101. </el-tooltip>
  102. <el-tooltip content="删除" placement="top">
  103. <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['business:versionManagement:remove']"
  104. >删除</el-button
  105. >
  106. </el-tooltip>
  107. <el-tooltip content="生成下载链接" placement="top">
  108. <el-button link type="primary" icon="Edit" @click="generateAppDownLoadFile2(scope.row)" v-hasPermi="['business:versionManagement:edit']"
  109. >生成下载链接</el-button
  110. >
  111. </el-tooltip>
  112. </template>
  113. </el-table-column>
  114. </el-table>
  115. <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
  116. </el-card>
  117. <!-- 添加或修改App版本管理对话框 -->
  118. <el-dialog :title="dialog.title" v-model="dialog.visible" width="700px" append-to-body>
  119. <el-form ref="versionManagementFormRef" :model="form" :rules="rules" label-width="100px">
  120. <el-form-item label="版本号" prop="versionCode">
  121. <el-input v-model="form.versionCode" placeholder="请输入版本号" />
  122. </el-form-item>
  123. <el-form-item label="版本名称" prop="versionName">
  124. <el-input v-model="form.versionName" placeholder="请输入版本名称" />
  125. </el-form-item>
  126. <el-form-item label="文件" prop="icon">
  127. <div class="upload-container">
  128. <el-upload
  129. class="upload-icon"
  130. action="#"
  131. :on-change="handleIconChange"
  132. :on-remove="handleIconRemove"
  133. :file-list="fileList"
  134. :auto-upload="false"
  135. :limit="1"
  136. accept=".apk,.ipa"
  137. >
  138. <template #trigger>
  139. <el-button type="primary">点击选择</el-button>
  140. </template>
  141. <template #tip>
  142. <div class="el-upload__tip">
  143. <span v-if="fileList.length > 0">当前已选文件:{{ fileList[0].name }}</span>
  144. </div>
  145. </template>
  146. </el-upload>
  147. </div>
  148. </el-form-item>
  149. <el-form-item label="下载链接" prop="downloadLink">
  150. <el-input v-model="form.downloadLink" type="textarea" placeholder="请输入内容" />
  151. </el-form-item>
  152. <el-form-item label="发布日期" prop="releaseDate">
  153. <el-date-picker clearable v-model="form.releaseDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择发布日期">
  154. </el-date-picker>
  155. </el-form-item>
  156. <el-form-item label="系统类型" prop="osType">
  157. <el-radio-group v-model="form.osType" size="small">
  158. <el-radio v-for="dict in mobile_sys_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
  159. </el-radio-group>
  160. </el-form-item>
  161. <el-form-item label="是否强制更新" prop="forceUpdate">
  162. <el-radio-group v-model="form.forceUpdate" size="small">
  163. <el-radio v-for="dict in is_force_update" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
  164. </el-radio-group>
  165. </el-form-item>
  166. <el-form-item label="更新内容" prop="updateContent">
  167. <editor v-model="form.updateContent" :min-height="192" />
  168. </el-form-item>
  169. <!-- <el-form-item label="已知问题" prop="knownIssues">
  170. <el-input v-model="form.knownIssues" type="textarea" placeholder="请输入内容" />
  171. </el-form-item>
  172. <el-form-item label="最低系统版本" prop="osSupport">
  173. <el-input v-model="form.osSupport" placeholder="请输入支持的最低系统版本,如 Android 5.0, iOS 11.0" />
  174. </el-form-item>-->
  175. <!-- <el-form-item label="更新包大小" prop="updatePackageSize">
  176. <el-input v-model="form.updatePackageSize" placeholder="请输入更新包大小" />
  177. </el-form-item>-->
  178. </el-form>
  179. <template #footer>
  180. <div class="dialog-footer">
  181. <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
  182. <el-button @click="cancel">取 消</el-button>
  183. </div>
  184. </template>
  185. </el-dialog>
  186. </div>
  187. </template>
  188. <script setup name="VersionManagement" lang="ts">
  189. import {
  190. listVersionManagement,
  191. getVersionManagement,
  192. delVersionManagement,
  193. addVersionManagement,
  194. updateVersionManagement,
  195. generateAppDownLoadFile,
  196. uploadAppFileAsync
  197. } from '@/api/system/business/versionManagement';
  198. import { VersionManagementVO, VersionManagementQuery, VersionManagementForm } from '@/api/system/business/versionManagement/types';
  199. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  200. const { is_force_update, mobile_sys_type } = toRefs<any>(proxy?.useDict('is_force_update', 'mobile_sys_type'));
  201. const versionManagementList = ref<VersionManagementVO[]>([]);
  202. const buttonLoading = ref(false);
  203. const loading = ref(true);
  204. const showSearch = ref(true);
  205. const ids = ref<Array<string | number>>([]);
  206. const single = ref(true);
  207. const multiple = ref(true);
  208. const total = ref(0);
  209. const queryFormRef = ref<ElFormInstance>();
  210. const versionManagementFormRef = ref<ElFormInstance>();
  211. const dialog = reactive<DialogOption>({
  212. visible: false,
  213. title: ''
  214. });
  215. const initFormData: VersionManagementForm = {
  216. id: undefined,
  217. versionCode: undefined,
  218. versionName: undefined,
  219. releaseDate: undefined,
  220. updateContent: undefined,
  221. knownIssues: undefined,
  222. osType: undefined,
  223. osSupport: undefined,
  224. forceUpdate: '0', // ✅ 默认选中“否”
  225. downloadLink: undefined,
  226. updatePackageSize: undefined,
  227. status: undefined,
  228. createdAt: undefined,
  229. updatedAt: undefined
  230. };
  231. const data = reactive<PageData<VersionManagementForm, VersionManagementQuery>>({
  232. form: { ...initFormData },
  233. queryParams: {
  234. pageNum: 1,
  235. pageSize: 10,
  236. versionCode: undefined,
  237. versionName: undefined,
  238. releaseDate: undefined,
  239. updateContent: undefined,
  240. knownIssues: undefined,
  241. osType: undefined,
  242. osSupport: undefined,
  243. forceUpdate: undefined,
  244. downloadLink: undefined,
  245. updatePackageSize: undefined,
  246. status: undefined,
  247. createdAt: undefined,
  248. updatedAt: undefined,
  249. params: {}
  250. },
  251. rules: {
  252. id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
  253. versionCode: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
  254. versionName: [{ required: true, message: '版本名称不能为空', trigger: 'blur' }],
  255. releaseDate: [{ required: true, message: '发布日期不能为空', trigger: 'blur' }],
  256. updateContent: [{ required: true, message: '更新内容不能为空', trigger: 'change' }],
  257. downloadLink: [{ required: false, message: '下载链接不能为空', trigger: 'blur' }],
  258. osType: [{ required: true, message: '系统类型不能为空', trigger: 'change' }]
  259. }
  260. });
  261. const { queryParams, form, rules } = toRefs(data);
  262. /** 查询App版本管理列表 */
  263. const getList = async () => {
  264. loading.value = true;
  265. const res = await listVersionManagement(queryParams.value);
  266. versionManagementList.value = res.rows;
  267. total.value = res.total;
  268. loading.value = false;
  269. };
  270. /** 取消按钮 */
  271. const cancel = () => {
  272. reset();
  273. fileList.value = [];
  274. dialog.visible = false;
  275. };
  276. /** 表单重置 */
  277. const reset = () => {
  278. form.value = { ...initFormData };
  279. versionManagementFormRef.value?.resetFields();
  280. };
  281. /** 搜索按钮操作 */
  282. const handleQuery = () => {
  283. queryParams.value.pageNum = 1;
  284. getList();
  285. };
  286. /** 重置按钮操作 */
  287. const resetQuery = () => {
  288. queryFormRef.value?.resetFields();
  289. handleQuery();
  290. };
  291. /** 多选框选中数据 */
  292. const handleSelectionChange = (selection: VersionManagementVO[]) => {
  293. ids.value = selection.map((item) => item.id);
  294. single.value = selection.length != 1;
  295. multiple.value = !selection.length;
  296. };
  297. /** 新增按钮操作 */
  298. const handleAdd = () => {
  299. reset();
  300. fileList.value = [];
  301. dialog.visible = true;
  302. dialog.title = '添加';
  303. };
  304. /** 修改按钮操作 */
  305. const handleUpdate = async (row?: VersionManagementVO) => {
  306. reset();
  307. const _id = row?.id || ids.value[0];
  308. const res = await getVersionManagement(_id);
  309. Object.assign(form.value, res.data);
  310. form.value.forceUpdate = String(res.data.forceUpdate);
  311. dialog.visible = true;
  312. dialog.title = '修改';
  313. };
  314. /** 提交按钮 */
  315. const submitForm = () => {
  316. versionManagementFormRef.value?.validate(async (valid: boolean) => {
  317. if (valid) {
  318. buttonLoading.value = true;
  319. if (form.value.id) {
  320. // 判断如果没有下载链接,则必须上传文件
  321. if (!form.value.downloadLink || form.value.downloadLink.trim() === '') {
  322. if (fileList.value.length === 0) {
  323. proxy?.$modal.msgError('请先选择并上传文件或填写下载链接');
  324. return;
  325. }
  326. }
  327. await updateVersionManagement(form.value).finally(() => (buttonLoading.value = false));
  328. } else {
  329. if (form.value.osType == 'Android') {
  330. if (fileList.value.length === 0) {
  331. proxy?.$modal.msgError('请先选择并上传文件');
  332. return;
  333. }
  334. }
  335. await addVersionManagement(form.value).finally(() => (buttonLoading.value = false));
  336. }
  337. proxy?.$modal.msgSuccess('操作成功');
  338. dialog.visible = false;
  339. await getList();
  340. }
  341. });
  342. };
  343. /** 删除按钮操作 */
  344. const handleDelete = async (row?: VersionManagementVO) => {
  345. const _ids = row?.id || ids.value;
  346. await proxy?.$modal.confirm('是否确认删除编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
  347. await delVersionManagement(_ids);
  348. proxy?.$modal.msgSuccess('删除成功');
  349. await getList();
  350. };
  351. const generateAppDownLoadFile2 = async (row?: VersionManagementVO) => {
  352. const _ids = row?.id || ids.value;
  353. await proxy?.$modal.confirm('是否生成下载链接?').finally(() => (loading.value = false));
  354. await generateAppDownLoadFile(_ids.toString());
  355. proxy?.$modal.msgSuccess('生成成功');
  356. await getList();
  357. };
  358. /** 导出按钮操作 */
  359. const handleExport = () => {
  360. proxy?.download(
  361. 'business/versionManagement/export',
  362. {
  363. ...queryParams.value
  364. },
  365. `versionManagement_${new Date().getTime()}.xlsx`
  366. );
  367. };
  368. const formatStatus = (status: number): string => {
  369. switch (status) {
  370. case 1:
  371. return '已发布';
  372. case 0:
  373. return '草稿';
  374. case -1:
  375. return '已下架';
  376. default:
  377. return '未知状态';
  378. }
  379. };
  380. const formatForceUpdate = (value: number): string => {
  381. return value === 1 ? '是' : '否';
  382. };
  383. onMounted(() => {
  384. getList();
  385. });
  386. //预览图标需要
  387. const iconPreviewUrl = ref('');
  388. const competitionIcon = ref('');
  389. const fileList = ref([]);
  390. const handleIconChange = async (file) => {
  391. const index = fileList.value.findIndex((f) => f.uid === file.uid);
  392. fileList.value = [];
  393. if (file.raw) {
  394. iconPreviewUrl.value = URL.createObjectURL(file.raw);
  395. }
  396. if (index === -1) {
  397. // 如果文件不在列表中,则添加进去
  398. fileList.value.push(file);
  399. }
  400. try {
  401. const rawFile = file.raw;
  402. const res = await uploadAppFileAsync(rawFile);
  403. if (res.code === 200) {
  404. // 更新文件状态为成功
  405. const uploadedFile = {
  406. ...file,
  407. status: 'success',
  408. response: res.data.url,
  409. fileName: res.data.fileName // 假设返回了 fileName 字段
  410. };
  411. // 找到刚添加的文件并更新
  412. const updatedIndex = fileList.value.findIndex((f) => f.uid === file.uid);
  413. if (updatedIndex !== -1) {
  414. fileList.value[updatedIndex] = uploadedFile;
  415. }
  416. // 设置下载链接
  417. form.value.downloadLink = 'https://oss.hunanpt.cn/' + uploadedFile.fileName;
  418. ElMessage.success('上传成功');
  419. } else {
  420. throw new Error(res.msg);
  421. }
  422. } catch (error) {
  423. // 更新文件状态为失败
  424. fileList.value[index] = {
  425. ...file,
  426. status: 'fail',
  427. error: '上传失败'
  428. };
  429. ElMessage.error('上传失败,请重试');
  430. }
  431. };
  432. // 删除文件处理函数
  433. const handleIconRemove = (file, updatedFileList) => {
  434. fileList.value = updatedFileList;
  435. // 清除预览图和临时链接
  436. iconPreviewUrl.value = '';
  437. competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
  438. };
  439. </script>
  440. <style scoped>
  441. .truncate {
  442. display: inline-block;
  443. max-width: 100%;
  444. overflow: hidden;
  445. text-overflow: ellipsis;
  446. white-space: nowrap;
  447. word-wrap: normal;
  448. }
  449. </style>