wangchen147 86198c273f 1.1.0
Niucloud JAVA版框架1.1.0更新内容
    新增
    * 增加小程序展示线上版本号
    * 发布小程序添加自定义版本号
    * 添加后台取消小程序绑定微信公众平台授权
    * 管理端核销功能
    * 用户端导入微信地址功能

    优化
    * 装修页面时可自定义底部导航
    * 小程序添加版权信息展示
    * 应用菜单结构优化:取消独立营销菜单,统一在应用菜单下按分类
    * 分页列表增加缓存,记录当前页信息,操作完成后,返回当前页
    * 开启标签栏时对标签页面进行缓存
    * 统一框架装修自定义使用底部导航数据结构

    修复
    * 删除表单报错
    * admin首页点击未安装、已安装应用,页面跳转空白
    * 修复插件云安装错误时同时显示安装失败和安装完成

商城1.0.5更新内容
    新增
    * 用户端添加订单开具发票功能
    * 管理后台添加补开发票功能

    优化
    * 商品列表增加相关活动的关联展示
    * 限时折扣、积分商城增加批量操作
    * 积分商品,若有规格未参与积分活动,则无法购买
    * 复制商品,提示语优化

    修复
    * 满减送活动赠品设置多件,只展示1件
    * 满减送赠品未扣除库存问题
    * 商品列表批量设置分类后商品分类查询不到商品的问题
    * 商品标签状态已经关闭添加商品时依旧可以选择
    * 商家地址库联系方式搜索无效
    * 商品分类拖动排序无效
    * 参与满减活动再使用优惠券,订单0元申请退款时,退款金额出现负数
    * 积分商城规格值设置不参与积分兑换提交订单未拦截
    * 虚拟商品核销码设置的永久有效,核销时提示商品已过期
    * 订单满减送退款售后点击详情报错
    * 按会员标签发放优惠券异常一直未发放
    * 微信支付申请退款售后点击转账跳转404
    * 订单列表输入会员编号、账号点击搜索筛选不出来内容
    * 多商品组商品来源选择分组,只有一个商品分类时多商品组商品展示不正确
    * 商品组件排序方式选择为价格时报错
    * 图文导航选择积分商品报错
2025-12-24 12:30:43 +08:00

493 lines
17 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="main-container">
<el-card class="card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-tabs v-model="activeName" class="my-[20px]" @tab-change="handleClick">
<el-tab-pane :label="t('weappAccessFlow')" name="/channel/weapp" />
<el-tab-pane :label="t('subscribeMessage')" name="/channel/weapp/message" />
<el-tab-pane :label="t('weappRelease')" name="/channel/weapp/code" />
</el-tabs>
<div class="mt-[20px]" v-if="!weappConfig.is_authorization">
<el-button type="primary" @click="openDialog" :loading="uploading" :disabled="loading">{{ t('cloudRelease') }}</el-button>
<el-button @click="localInsert" :disabled="loading">{{ t('localRelease') }}</el-button>
</div>
<div class="mt-[20px]" v-else>
<el-button type="primary" @click="againUpload" :loading="uploading" :disabled="loading">{{ t('uploadWeapp') }}</el-button>
</div>
<div class="text-[14px] mt-[15px]">
<div class="flex items-center">
<div v-if="weappTableData.version_info.release_version">
线上版本: <span class="mr-10 text-primary">{{ weappTableData.version_info.release_version }}</span>
</div>
<div v-if="weappTableData.version_info.release_time">
发布时间: <span >{{ weappTableData.version_info.release_time }}</span>
</div>
</div>
<div class="flex items-center">
<div v-if="weappTableData.version_info.exp_version" class="mt-2">
体验版本: <span class="mr-10 text-primary">{{ weappTableData.version_info.exp_version }}</span>
</div>
<div v-if="weappTableData.version_info.exp_time" class="mt-2">
过期时间: <span >{{ weappTableData.version_info.exp_time }}</span>
</div>
</div>
</div>
<el-table class="mt-[15px]" :data="weappTableData.data" v-loading="weappTableData.loading" size="default">
<template #empty>
<span>{{ t('emptyData') }}</span>
</template>
<el-table-column prop="version" :label="t('code')" align="left" />
<el-table-column prop="status_name" :label="t('status')" align="left">
<template #default="{ row }">
<div>{{ row.status_name }}</div>
</template>
</el-table-column>
<el-table-column prop="create_time" :label="t('createTime')" align="center" />
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row, $index }">
<template v-if="previewContent && $index == 0 && (row.status == 1 || row.status == 2) && weappTableData.page == 1">
<el-tooltip :content="previewContent" raw-content effect="light">
<el-button type="primary" link>{{ t('preview') }}</el-button>
</el-tooltip>
</template>
<el-button type="primary" link v-if="row.status == -1 || row.status == -2" @click="handleFailReason(row)">{{ t('failReason') }}</el-button>
<el-button type="primary" link v-if="row.status == -2" @click="againUpload(row)" :loading="uploading">{{ t('againUpload') }}</el-button>
<el-button type="primary" link v-if="row.status == 2" @click="undoAuditFn(row)">{{ t('undoAudit') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="weappTableData.page" v-model:page-size="weappTableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="weappTableData.total"
@size-change="getWeappVersionListFn()" @current-change="getWeappVersionListFn" />
</div>
</el-card>
<el-dialog v-model="dialogVisible" :title="t('codeDownTwoDesc')" width="30%" :before-close="handleClose">
<el-form ref="ruleFormRef" :model="form" label-width="120px">
<el-form-item prop="code" :label="t('code')">
<el-input v-model.trim="form.code" :placeholder="t('codePlaceholder')" onkeyup="this.value = this.value.replace(/[^\d\.]/g,'');" />
</el-form-item>
<el-form-item prop="path" :label="t('path')">
<upload-file v-model="form.path" :api="'weapp/upload'" :accept="'.zip'" />
</el-form-item>
<el-form-item :label="t('content')">
<el-input type="textarea" v-model.trim="form.content" :placeholder="t('contentPlaceholder')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="insert">
{{ t('confirm') }}
</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="cloudVersionDialogVisible" :title="t('codeDownTwoDesc')" width="600px" :before-close="handleCloseCloudVersion">
<el-form ref="cloudRuleFormRef" :model="form" :rules="formRules" label-width="120px">
<el-form-item prop="type" :label="t('版本号类型')">
<div>
<el-radio-group v-model="form.type">
<el-radio :label="1">{{ t('默认') }}</el-radio>
<el-radio :label="2">{{ t('自定义') }}</el-radio>
</el-radio-group>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">默认为列表版本号递增自定义则为手动输入版本号进行上传首位必须大于1</div>
</div>
</el-form-item>
<el-form-item prop="version" :label="t('code')" v-if="form.type == 2">
<div class="flex items-end">
<el-form-item prop="code1">
<el-input v-model.number="form.code1" class="!w-[70px]" :placeholder="t('codePlaceholder')" />
</el-form-item>
<span class="mx-[10px]">.</span>
<el-form-item prop="code2">
<el-input v-model.number="form.code2" class="!w-[70px]" :placeholder="t('codePlaceholder')" />
</el-form-item>
<span class="mx-[10px]">.</span>
<el-form-item prop="code3">
<el-input v-model.number="form.code3" class="!w-[70px]" :placeholder="t('codePlaceholder')" />
</el-form-item>
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseCloudVersion">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save">
{{ t('confirm') }}
</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="failReasonDialogVisible" :title="t('failReason')" width="60%">
<el-scrollbar class="h-[60vh] w-full whitespace-pre-wrap p-[20px]">
<div v-html="failReason"></div>
</el-scrollbar>
</el-dialog>
<el-dialog v-model="uploadSuccessShowDialog" :title="t('warning')" width="500px" draggable>
<span v-html="t('uploadSuccessTips')"></span>
<template #footer>
<div class="flex justify-end">
<el-button @click="knownToKnow" type="primary">{{ t('knownToKnow') }}</el-button>
<el-button @click="uploadSuccessShowDialog = false" type="primary" plain>{{ t('confirm') }}</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { setWeappVersion, getWeappPreview, getWeappVersionList, getWeappUploadLog, getWeappConfig } from '@/app/api/weapp'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { getAuthInfo } from '@/app/api/module'
import { getAppType } from '@/utils/common'
import { ElMessageBox } from 'element-plus'
import { AnyObject } from '@/types/global'
import Storage from '@/utils/storage'
import { siteWeappCommit, undoAudit } from "@/app/api/wxoplatform";
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const dialogVisible = ref(false)
const loading = ref(true)
const weappTableData:{
page: number,
limit: number,
total: number,
loading: boolean,
data: AnyObject,
version_info: AnyObject
} = reactive({
page: 1,
limit: 10,
total: 0,
loading: false,
data: [],
version_info: {}
})
const form = ref({
type:1,
version: '',
code1: '1',
code2: '0',
code3: '0'
})
const uploadSuccessShowDialog = ref(false)
const authCode = ref('')
getAuthInfo().then(res => {
if (res.data.data && res.data.data.auth_code) {
authCode.value = res.data.data.auth_code
getWeappPreviewImage()
}
loading.value = false
}).catch(() => {
loading.value = false
})
const weappConfig = ref<{
app_id:string,
app_secret:string,
is_authorization: number
}>({
app_id: '',
app_secret: '',
is_authorization: 0
})
getWeappConfig().then(res => {
weappConfig.value = res.data
})
const activeName = ref('/channel/weapp/code')
const handleClick = (val: any) => {
router.push({ path: activeName.value })
}
const ruleFormRef = ref<any>(null)
const cloudRuleFormRef = ref<any>(null)
// 表单校验规则
const formRules = reactive({
code1: [
{
validator: (rule: any, value: number, callback: any) => {
if (value < 1) {
callback(new Error(t('必须大于1')));
} else {
callback();
}
},
trigger: 'blur'
}
],
code2: [
{
validator: (rule: any, value: number, callback: any) => {
if (value < 0) {
callback(new Error(t('必须大于0')));
} else {
callback();
}
},
trigger: 'blur'
}
],
code3: [
{
validator: (rule: any, value: number, callback: any) => {
if (value < 0) {
callback(new Error(t('必须大于0')));
} else {
callback();
}
},
trigger: 'blur'
}
],
version:[
{
required: true,
validator: (rule: any, value: string, callback: any) => {
if(form.value.type == 2){
if(!form.value.code1 || !form.value.code2 || !form.value.code3){
callback(new Error(t('请填写版本号')));
}else{
callback();
}
}else{
callback();
}
}
}
]
});
/**
* 获取版本列表
*/
const getWeappVersionListFn = (page: number = 1) => {
weappTableData.loading = true
weappTableData.page = page
getWeappVersionList({
page: weappTableData.page,
limit: weappTableData.limit
}).then(res => {
weappTableData.loading = false
weappTableData.data = res.data.data
weappTableData.total = res.data.total
if (page == 1 && weappTableData.data.length && weappTableData.data[0].status == 0) getWeappUploadLogFn(weappTableData.data[0].task_key)
weappTableData.version_info = res.data.version_info
}).catch(() => {
weappTableData.loading = false
})
}
getWeappVersionListFn()
const openDialog = () => {
if (!authCode.value) {
authElMessageBox()
return
}
if (!weappConfig.value.app_id) {
configElMessageBox()
return
}
form.value = {
type:1,
version: '',
code1: '1',
code2: '0',
code3: '0'
}
cloudVersionDialogVisible.value = true
}
const handleClose = () => {
ruleFormRef.value.clearValidate()
}
const cloudVersionDialogVisible = ref(false)
const handleCloseCloudVersion = () => {
cloudVersionDialogVisible.value = false
form.value = {
type:1,
version: '',
code1: '1',
code2: '0',
code3: '0'
}
}
const save = () => {
cloudRuleFormRef.value.validate((valid: boolean) => {
if (valid) {
if (form.value.type == 2) {
form.value.version = `${form.value.code1}.${form.value.code2}.${form.value.code3}`
}
delete form.value.code1
delete form.value.code2
delete form.value.code3
delete form.value.type
cloudVersionDialogVisible.value = false
insert()
}
})
}
const uploading = ref(false)
const insert = () => {
if (!authCode.value) {
authElMessageBox()
return
}
if (!weappConfig.value.app_id) {
configElMessageBox()
return
}
if (uploading.value) return
uploading.value = true
previewContent.value = ''
setWeappVersion(form.value).then(res => {
getWeappVersionListFn()
getWeappPreviewImage()
uploading.value = false
}).catch(() => {
uploading.value = false
})
}
const localInsert = () => {
ElMessageBox.alert(t('localInsertTips'), t('warning'), {
confirmButtonText: t('confirm')
})
}
const previewContent = ref('')
const getWeappPreviewImage = () => {
if (!authCode.value) return
getWeappPreview().then(res => {
if (res.data) previewContent.value = `<img src="${ res.data }" class="w-[150px]">`
}).catch()
}
const getWeappUploadLogFn = (key: string) => {
getWeappUploadLog(key).then(res => {
const data = res.data.data ?? []
if (data[0] && data[0].length) {
const last = data[0][data[0].length - 1]
if (last.code == 0) {
getWeappVersionListFn()
return
}
if (last.code == 1 && last.percent == 100) {
getWeappVersionListFn()
getWeappPreviewImage()
!Storage.get('weappUploadTipsLock') && (uploadSuccessShowDialog.value = true)
return
}
setTimeout(() => {
getWeappUploadLogFn(key)
}, 2000)
}
})
}
const authElMessageBox = () => {
if (getAppType() == 'admin') {
ElMessageBox.confirm(
t('authTips'),
t('warning'),
{
distinguishCancelAndClose: true,
confirmButtonText: t('toBind'),
cancelButtonText: t('toNiucloud')
}
).then(() => {
router.push({ path: '/app/authorize' })
}).catch((action: string) => {
if (action === 'cancel') {
window.open('https://www.niucloud.com/app')
}
})
} else {
ElMessageBox.alert(t('siteAuthTips'), t('warning'))
}
}
const configElMessageBox = () => {
ElMessageBox.confirm(
t('weappTips'),
t('warning'),
{
confirmButtonText: t('toSetting'),
cancelButtonText: t('cancel')
}
).then(() => {
router.push({ path: '/channel/weapp/config' })
})
}
/**
* 撤回代码审核
* @param data
*/
const undoAuditFn = (data: any) => {
ElMessageBox.confirm(
t('undoAuditTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel')
}
).then(() => {
undoAudit({ id: data.id }).then(() => {
getWeappVersionListFn()
})
})
}
const failReason = ref('')
const failReasonDialogVisible = ref(false)
const handleFailReason = (data: any) => {
failReason.value = data.fail_reason
failReasonDialogVisible.value = true
}
const knownToKnow = () => {
Storage.set({ key: 'weappUploadTipsLock', data: true })
uploadSuccessShowDialog.value = false
}
const againUpload = () => {
if (uploading.value) return
uploading.value = true
siteWeappCommit().then(() => {
getWeappVersionListFn()
getWeappPreviewImage()
uploading.value = false
}).catch(() => {
uploading.value = false
})
}
</script>
<style lang="scss" scoped></style>