Files
sunProduct/src/views/system/version/index.vue
2026-08-31 18:00:11 +08:00

396 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="p-2 h-full flex flex-col">
<pageheader></pageheader>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :inline="true" :model="queryParams">
<el-form-item label="应用名称" prop="channel">
<el-select v-model="queryParams.channel" placeholder="请选择">
<el-option v-for="(item, index) in com_app_list" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="应用类型" prop="platform">
<el-select v-model="queryParams.platform" placeholder="请选择">
<el-option label="iOS" value="ios"></el-option>
<el-option label="Android" value="android"></el-option>
</el-select>
</el-form-item>
<el-form-item label="版本号" prop="versionCode">
<el-input v-model.trim="queryParams.versionCode" clearable maxlength="20" placeholder="请输入版本号" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="版本名称" prop="versionName">
<el-input v-model.trim="queryParams.versionName" clearable maxlength="20" placeholder="请输入版本名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button icon="Search" type="primary" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card class="flex-1" shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-has-permi="['system:version:add']" icon="Plus" plain type="primary" @click="handleAdd">新增</el-button>
</el-col>
</el-row>
</template>
<el-table
v-loading="loading"
:data="versionList"
:default-sort="{ prop: 'versionCode', order: 'descending' }"
border
@selection-change="handleSelectionChange"
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="应用名称" prop="appChannel" sortable>
<template #default="scope">
<dict-tag :options="com_app_list" :value="scope.row.appChannel"></dict-tag>
</template>
</el-table-column>
<el-table-column align="center" label="版本类型" prop="platform">
<template #default="scope">
<el-tag v-if="scope.row.platform === 'ios'" type="success">ios</el-tag>
<el-tag v-else type="primary">android</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="更新渠道" prop="channel" sortable />
<el-table-column align="center" label="版本号" prop="versionCode" sortable />
<el-table-column align="center" label="版本名称" prop="versionName" sortable />
<el-table-column align="center" label="最低支持版本" prop="minVersionName" sortable />
<el-table-column align="center" label="下载地址" prop="downloadUrl" />
<el-table-column align="center" label="更新日志" prop="releaseNotes" />
<el-table-column align="center" label="停用/启用" prop="status">
<template #default="scope">
<el-switch
v-model="scope.row.status"
active-value="1"
inactive-value="0"
@change="(val: string) => switchVersion(scope.row.id, val)"
></el-switch>
</template>
</el-table-column>
<el-table-column align="center" label="上传时间" prop="createTime" />
</el-table>
<pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" append-to-body width="650px">
<el-form ref="versionFormRef" :model="form" :rules="rules" label-width="130px">
<el-form-item label="App端" prop="appChannel">
<el-select v-model="form.appChannel" placeholder="请选择" @change="handleGetVersionlast">
<el-option v-for="(item, index) in com_app_list" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="App类型" prop="platform">
<el-radio-group v-model="form.platform" @change="handleChangePlatform">
<el-radio :value="appType.android">Android</el-radio>
<el-radio :value="appType.ios">ios</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="版本名称" prop="versionName">
<el-input v-model.trim="form.versionName" maxlength="20" placeholder="请输入版本名称例如:1.2.0" />
</el-form-item>
<el-form-item label="最低支持版本" prop="minVersionCode">
<el-select v-if="getlastVersionlist.length > 0" v-model="form.minVersionCode" placeholder="请输入最低支持版本">
<el-option v-for="(item, index) in getlastVersionlist" :key="index" :label="item.versionName" :value="item.versionCode"></el-option>
</el-select>
<el-select v-else v-model="form.minVersionCode" placeholder="请输入最低支持版本">
<el-option label="1.0.0" value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="渠道" prop="channel">
<el-radio-group v-model="form.channel" @change="handleChangeChannel">
<el-radio v-if="form.platform !== appType.ios" value="huawei">华为</el-radio>
<el-radio v-if="form.platform !== appType.ios" value="xiaomi">小米</el-radio>
<el-radio v-if="form.platform !== appType.ios" value="oppo">oppo</el-radio>
<el-radio v-if="form.platform !== appType.ios" value="vivo">vivo</el-radio>
<el-radio v-if="form.platform !== appType.ios" value="yyb">应用宝</el-radio>
<el-radio v-if="form.platform !== appType.ios" value="honor">荣耀</el-radio>
<el-radio v-else value="ios">ios</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="更新日志" prop="releaseNotes">
<el-input v-model="form.releaseNotes" :rows="5" maxlength="200" placeholder="请输入内容" show-word-limit type="textarea" />
</el-form-item>
<!-- <el-form-item label="低版本强制更新">-->
<!-- <div>-->
<!-- <el-switch-->
<!-- v-model="form.updateType"-->
<!-- :active-value="updateType.force"-->
<!-- active-text="开启"-->
<!-- :inactive-value="updateType.optional"-->
<!-- inactive-text="停止"-->
<!-- ></el-switch>-->
<!-- <p class="tips">-->
<!-- <strong style="color: #000">配合最低支持版本使用</strong> 开启后: <br />低于最低版→启用强制(无取消,不更退出 APP);<br />高于最低版→普通弹窗可选取消。-->
<!-- </p>-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="更新地址" prop="downloadUrl" v-if="form.platform === appType.ios">-->
<!-- <el-input v-model="form.downloadUrl" placeholder="请输入App Store 链接" />-->
<!-- </el-form-item>-->
<!-- <div v-else-if="form.platform === appType.android">-->
<!-- <el-form-item label="更新类型">-->
<!-- <el-radio-group v-model="form.method">-->
<!-- <el-radio-button :value="updatePackageType.apk">整包更新(apk)</el-radio-button>-->
<!-- <el-radio-button :value="updatePackageType.wgt">热更新(wgt)</el-radio-button>-->
<!-- </el-radio-group>-->
<!-- </el-form-item>-->
<el-form-item label="上传安装包地址" prop="downloadUrl">
<el-input v-model.trim="form.downloadUrl" maxlength="20" placeholder="请输入更新地址" />
</el-form-item>
<!-- </div>-->
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" name="Version" setup>
import { listVersion, addVersion, getPastVersion, switchVersion } from '@/api/system/version';
import { VersionVO, VersionQuery, VersionForm } from '@/api/system/version/type';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_app_list } = toRefs<any>(proxy?.useDict('com_app_list'));
const versionList = ref<VersionVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const versionFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: VersionForm = {
id: undefined,
platform: 'android',
versionCode: undefined,
versionName: undefined,
minVersionCode: '1.0.0',
updateType: undefined,
method: 'apk',
downloadUrl: undefined,
releaseNotes: undefined,
fileSize: undefined,
appChannel: 'resident',
status: undefined,
channel: undefined
};
const data = reactive<PageData<VersionForm, VersionQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
platform: undefined,
versionCode: undefined,
versionName: undefined,
appChannel: undefined
},
rules: {
channel: [{ required: true, message: 'App渠道不能为空', trigger: 'blur' }],
platform: [{ required: true, message: 'App类型不能为空', trigger: 'blur' }],
versionCode: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
versionName: [{ required: true, message: '版本名称不能为空', trigger: 'blur' }],
downloadUrl: [{ required: true, message: '安装包地址不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
enum appType {
/** ios */
ios = 'ios',
/** android */
android = 'android'
}
/** 更新包类型 */
enum updatePackageType {
/** 整包更新 */
apk = 'apk'
/** 热更新 */
// wgt = 'wgt'
}
enum updateType {
/** 强制更新 */
// force = '0',
/** 可选更新 */
optional = '1'
}
const getlastVersionlist = ref([]);
/**
* 判断当前版本是否需要更新到最新版本。
* 比较逻辑为:如果当前版本号小于或等于最新版本号,则认为需要更新。
*
* @param {string} current - 当前版本号字符串,格式为点分十进制(如 "1.2.3")
* @param {string} latest - 最新版本号字符串,格式为点分十进制(如 "1.2.4")
* @returns {boolean} 如果当前版本小于或等于最新版本,返回 true;否则返回 false
*/
// function needUpdate(current: string, latest: string) {
// // 将版本号字符串分割并转换为数字数组
// const c = current.split('.').map(Number);
// const l = latest.split('.').map(Number);
// const max = Math.max(c.length, l.length);
//
// // 逐段比较版本号,若当前段小于最新段则需更新,大于则无需更新
// for (let i = 0; i < max; i++) {
// const cv = c[i] || 0;
// const lv = l[i] || 0;
// if (cv <= lv) return true;
// if (cv > lv) return false;
// }
// return false;
// }
/** 查询【请填写功能名称】列表 */
const getList = async () => {
loading.value = true;
const res = await listVersion(queryParams.value);
versionList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
versionFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
form.value = { ...initFormData };
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: VersionVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加版本';
};
function handleChangePlatform() {
form.value.minVersionCode = '1.0.0';
form.value.updateType = updateType.optional;
form.value.method = updatePackageType.apk;
form.value.downloadUrl = '';
form.value.channel = '';
if (form.value.platform === 'ios') {
form.value.channel = 'ios';
handleChangeChannel(form.value.channel);
}
handleGetVersionlast();
}
function handleGetVersionlast() {
getPastVersion(form.value.platform, form.value.appChannel).then((res) => {
getlastVersionlist.value = res.data;
form.value.minVersionCode = getlastVersionlist.value[getlastVersionlist.value.length - 1]
? getlastVersionlist.value[getlastVersionlist.value.length - 1].versionCode
: null;
});
}
const channelUrl = {
'huawei': 'tappmarket://details?id=',
'xiaomi': 'mimarket://details?id=',
'oppo': 'oppomarket://details?packagename=',
'vivo': 'vivomarket://details?id=',
'honor': 'market://details?id=',
'yyb': 'tmast://appdetails?pname=',
'ios': 'itms-apps://apps.apple.com/cn/app/id'
};
function handleChangeChannel(val: string) {
form.value.downloadUrl = channelUrl[val];
}
/** 提交按钮 */
const submitForm = () => {
if (!form.value.minVersionCode) {
ElMessage.error('请选择最低支持版本');
return;
}
versionFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
await addVersion(form.value).finally(() => (buttonLoading.value = false));
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
onMounted(() => {
getList();
});
</script>
<style lang="scss" scoped>
.el-table {
height: calc(100% - 80px);
}
.uploadFileBox {
width: 100px;
height: 100px;
border-radius: 4px 4px 4px 4px;
background-color: rgba(255, 255, 255, 1);
color: rgba(16, 16, 16, 1);
border: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover {
border: 1px solid #409eff;
color: #409eff;
}
}
.tips {
color: #ccc;
font-size: 12px;
}
</style>