mirror of
https://gitee.com/niucloud-team/niucloud-admin.git
synced 2026-03-17 11:13:43 +00:00
up admin
This commit is contained in:
parent
c337da9ea3
commit
d98c2dacc1
@ -1,6 +1,5 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录
|
* 登录
|
||||||
* @param params
|
* @param params
|
||||||
@ -24,14 +23,6 @@ export function getAuthMenus(params: Record<string, any>) {
|
|||||||
return request.get('auth/authmenu', { params })
|
return request.get('auth/authmenu', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取登录用户权限
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function getSiteInfo() {
|
|
||||||
return request.get('auth/site')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取登录配置信息
|
* 获取登录配置信息
|
||||||
* @returns
|
* @returns
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export function getDiyFormPageList(params: Record<string, any>) {
|
|||||||
export function getDiyFormList(params: Record<string, any>) {
|
export function getDiyFormList(params: Record<string, any>) {
|
||||||
return request.get(`diy/form/list`, { params })
|
return request.get(`diy/form/list`, { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取万能表单分页列表(用于弹框选择)
|
* 获取万能表单分页列表(用于弹框选择)
|
||||||
* @param params
|
* @param params
|
||||||
|
|||||||
@ -61,3 +61,11 @@ export function getFrameworkNewVersion() {
|
|||||||
export function getFrameworkVersionList() {
|
export function getFrameworkVersionList() {
|
||||||
return request.get(`niucloud/framework/version/list`)
|
return request.get(`niucloud/framework/version/list`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用/插件的版本更新记录
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function getAppVersionList(params: Record<string, any>) {
|
||||||
|
return request.get(`niucloud/app_version/list`, { params })
|
||||||
|
}
|
||||||
|
|||||||
@ -54,6 +54,14 @@ export function getPayRefundInfo(refund_no: string) {
|
|||||||
return request.get(`pay/refund/${refund_no}`)
|
return request.get(`pay/refund/${refund_no}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取退款状态字典
|
||||||
|
* @param refund_no
|
||||||
|
*/
|
||||||
|
export function getRefundStatus() {
|
||||||
|
return request.get(`pay/refund/status`)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退款方式
|
* 退款方式
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -727,3 +727,11 @@ export function deleteExport(id: number) {
|
|||||||
export function getInstallConfig() {
|
export function getInstallConfig() {
|
||||||
return request.get('sys/install/config')
|
return request.get('sys/install/config')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取二维码
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getQrcode(params: Record<string, any>) {
|
||||||
|
return request.get(`sys/qrcode`, { params, showErrorMessage: false })
|
||||||
|
}
|
||||||
|
|||||||
@ -85,6 +85,7 @@ export function addonDevelopBuild(key: any) {
|
|||||||
export function addonDevelopDownload(key: any) {
|
export function addonDevelopDownload(key: any) {
|
||||||
return request.post(`addon_develop/download/${ key }`, {})
|
return request.post(`addon_develop/download/${ key }`, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************** 代码生成 ****************************************************/
|
/***************************************************** 代码生成 ****************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -20,15 +20,15 @@ export function getUpgradeTask() {
|
|||||||
* 升级
|
* 升级
|
||||||
* @param addon
|
* @param addon
|
||||||
*/
|
*/
|
||||||
export function upgradeAddon(addon: string = '') {
|
export function upgradeAddon(addon: string = '', params: Record<string, any> = {}) {
|
||||||
return request.post(addon ? `upgrade/${addon}` : 'upgrade')
|
return request.post(addon ? `upgrade/${ addon }` : 'upgrade', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行升级
|
* 执行升级
|
||||||
*/
|
*/
|
||||||
export function executeUpgrade() {
|
export function executeUpgrade() {
|
||||||
return request.post('upgrade/execute', {})
|
return request.post('upgrade/execute', {}, { showErrorMessage: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,3 +44,96 @@ export function preUpgradeCheck(addon: string = '') {
|
|||||||
export function clearUpgradeTask() {
|
export function clearUpgradeTask() {
|
||||||
return request.post('upgrade/clear')
|
return request.post('upgrade/clear')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户操作
|
||||||
|
* @param operate
|
||||||
|
*/
|
||||||
|
export function upgradeUserOperate(operate: string) {
|
||||||
|
return request.post(`upgrade/operate/${ operate }`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取升级记录分页列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getUpgradeRecords(params: Record<string, any>) {
|
||||||
|
return request.get(`upgrade/records`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除升级记录
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function delUpgradeRecords(params: Record<string, any>) {
|
||||||
|
return request.delete(`upgrade/records`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取备份记录分页列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getBackupRecords(params: Record<string, any>) {
|
||||||
|
return request.get(`backup/records`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改备份备注
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function modifyBackupRemark(params: Record<string, any>) {
|
||||||
|
return request.put(`backup/remark`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份功能 检测目录权限
|
||||||
|
*/
|
||||||
|
export function checkDirExist(params: Record<string, any>) {
|
||||||
|
return request.post('backup/check_dir', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份功能 检测目录权限
|
||||||
|
*/
|
||||||
|
export function checkPermission(params: Record<string, any>) {
|
||||||
|
return request.post('backup/check_permission', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份功能 恢复升级备份
|
||||||
|
*/
|
||||||
|
export function restoreUpgradeBackup(params: Record<string, any>) {
|
||||||
|
return request.post('backup/restore', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份功能 删除升级记录
|
||||||
|
*/
|
||||||
|
export function deleteRecords(params: Record<string, any>) {
|
||||||
|
return request.post('backup/delete', params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动备份
|
||||||
|
*/
|
||||||
|
export function manualBackup(params: Record<string, any>) {
|
||||||
|
return request.post("backup/manual", params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取进行中的恢复
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function performRecoveryTasks(params: Record<string, any>) {
|
||||||
|
return request.get("backup/restore_task", params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取进行中的备份
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function performBackupTasks(params: Record<string, any>) {
|
||||||
|
return request.get("backup/task", params)
|
||||||
|
}
|
||||||
|
|||||||
@ -66,3 +66,19 @@ export function addVerifier(params: Record<string, any>) {
|
|||||||
export function deleteVerifier(id: number) {
|
export function deleteVerifier(id: number) {
|
||||||
return request.delete(`verify/verifier/${ id }`, { showSuccessMessage: true })
|
return request.delete(`verify/verifier/${ id }`, { showSuccessMessage: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取核销员信息
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getVerifyInfo(id: number) {
|
||||||
|
return request.get(`verify/verifier/${ id }`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改核销员信息
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function editVerifier(params: Record<string, any>) {
|
||||||
|
return request.post(`verify/verifier/${ params.id }`, params,{ showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ export function getTemplateList() {
|
|||||||
export function getBatchAcquisition(params: Record<string, any>) {
|
export function getBatchAcquisition(params: Record<string, any>) {
|
||||||
return request.put('weapp/template/sync', params, { showSuccessMessage: true })
|
return request.put('weapp/template/sync', params, { showSuccessMessage: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加微信小程序版本
|
* 添加微信小程序版本
|
||||||
* @param params
|
* @param params
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 74 KiB |
@ -84,7 +84,6 @@ const active = ref('build')
|
|||||||
const cloudBuildCheck = ref<null | AnyObject>(null)
|
const cloudBuildCheck = ref<null | AnyObject>(null)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const terminalRef = ref(null)
|
const terminalRef = ref(null)
|
||||||
const emits = defineEmits(['complete'])
|
|
||||||
|
|
||||||
let cloudBuildLog = []
|
let cloudBuildLog = []
|
||||||
/**
|
/**
|
||||||
@ -127,7 +126,7 @@ const getCloudBuildLogFn = () => {
|
|||||||
|
|
||||||
data[0].forEach(item => {
|
data[0].forEach(item => {
|
||||||
if (!cloudBuildLog.includes(item.action)) {
|
if (!cloudBuildLog.includes(item.action)) {
|
||||||
terminalRef.value.pushMessage({ content: `正在执行:${item.action}` })
|
terminalRef.value.pushMessage({ content: `${item.action}` })
|
||||||
cloudBuildLog.push(item.action)
|
cloudBuildLog.push(item.action)
|
||||||
|
|
||||||
if (item.code == 0) {
|
if (item.code == 0) {
|
||||||
|
|||||||
240
admin/src/app/components/upgrade-log/index.vue
Normal file
240
admin/src/app/components/upgrade-log/index.vue
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisible" :title="t('gxx')" width="850" :destroy-on-close="true">
|
||||||
|
<el-card class="box-card !border-none" shadow="never" >
|
||||||
|
<div v-loading="loading">
|
||||||
|
<div class="text-page-title mb-[20px]">历史版本</div>
|
||||||
|
<div class="time-dialog h-[500px]" style="overflow: auto">
|
||||||
|
<el-scrollbar>
|
||||||
|
<el-timeline style="width: 100%" v-if="!loading">
|
||||||
|
<el-timeline-item v-for="(item, index) in frameworkVersionList" :key="index" placement="left" :color="color">
|
||||||
|
<div class="relative">
|
||||||
|
<span class="text-[#333333] text-[14px] absolute">{{ timeSplit(item.release_time)[0] }}</span>
|
||||||
|
<br />
|
||||||
|
<span class="text-[#999999] text-[14px] w-[78px] block mt-[10px] absolute text-right">{{ timeSplit(item.release_time)[1] }}</span>
|
||||||
|
</div>
|
||||||
|
<el-collapse v-model="activeName" accordion>
|
||||||
|
<el-collapse-item :name="index">
|
||||||
|
<template #title>
|
||||||
|
<span class="text-[#333] text-[14px]"> v{{ item.version_no }} </span>
|
||||||
|
</template>
|
||||||
|
<div class="px-[20px] py-[20px] bg-overlay timeline-log-wrap whitespace-pre-wrap rounded-[4px]" style="background: rgba(25, 103, 249, 0.03);" v-if="item['upgrade_log']">
|
||||||
|
<div v-html="item['upgrade_log']"></div>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref, defineProps, nextTick } from "vue"
|
||||||
|
import { t } from "@/lang"
|
||||||
|
import { getAppVersionList, getFrameworkVersionList } from "@/app/api/module"
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
upgradeKey: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const frameworkVersionList = ref([])
|
||||||
|
|
||||||
|
const newVersion: any = computed(() => {
|
||||||
|
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
|
||||||
|
})
|
||||||
|
|
||||||
|
const getAppVersionListFn = () => {
|
||||||
|
getAppVersionList({ app_key: props.upgradeKey }).then(({ data }) => {
|
||||||
|
loading.value = false
|
||||||
|
data.forEach((item: any, index) => {
|
||||||
|
if (index == 0) {
|
||||||
|
item.important = 1
|
||||||
|
} else {
|
||||||
|
item.important = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
frameworkVersionList.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getFrameworkVersionListFn = () => {
|
||||||
|
getFrameworkVersionList().then(({ data }) => {
|
||||||
|
loading.value = false
|
||||||
|
data.forEach((item: any, index) => {
|
||||||
|
if (index == 0) {
|
||||||
|
item.important = 1
|
||||||
|
} else {
|
||||||
|
item.important = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
frameworkVersionList.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeName = ref(0)
|
||||||
|
|
||||||
|
// 提交信息
|
||||||
|
const loading = ref(true)
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const open = async () => {
|
||||||
|
nextTick(() => {
|
||||||
|
activeName.value = 0 // 重置
|
||||||
|
|
||||||
|
if (props.upgradeKey) {
|
||||||
|
getAppVersionListFn()
|
||||||
|
} else {
|
||||||
|
getFrameworkVersionListFn()
|
||||||
|
}
|
||||||
|
dialogVisible.value = true
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
const timeSplit = (str: string) => {
|
||||||
|
const [date, time] = str.split(" ")
|
||||||
|
const [hours, minutes] = time.split(":")
|
||||||
|
return [date, `${ hours }:${ minutes }`]
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
<style scoped>
|
||||||
|
.el-timeline-item {
|
||||||
|
min-height: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-timeline-item >>> .el-timeline-item__node--normal {
|
||||||
|
left: 117px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
|
||||||
|
background: rgba(25, 103, 249, 0.12) !important;
|
||||||
|
border-radius: 50%;
|
||||||
|
/* 创建圆形 */
|
||||||
|
position: relative;
|
||||||
|
/* 用于定位伪元素 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-timeline-item >>> .el-timeline-item__node--normal::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 8px;
|
||||||
|
/* 中心圆直径 */
|
||||||
|
height: 8px;
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
/* 中心圆颜色 */
|
||||||
|
border-radius: 50%;
|
||||||
|
/* 中心圆为圆形 */
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
/* 居中显示 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-timeline-item >>> .el-timeline-item__tail {
|
||||||
|
left: 125px;
|
||||||
|
border-left-color: #dddddd;
|
||||||
|
border-left-style: dashed;
|
||||||
|
margin: 12px 0;
|
||||||
|
margin-top: 24px;
|
||||||
|
height: calc(100% - 24px - 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-dialog__header {
|
||||||
|
padding: 10px 20px;
|
||||||
|
height: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
text-align: left;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: solid 1px #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-card__body {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-card.is-always-shadow {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-dialog__headerbtn .el-dialog__close {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-dialog__headerbtn:hover .el-dialog__close {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-dialog__headerbtn {
|
||||||
|
top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-collapse {
|
||||||
|
margin-left: 119px;
|
||||||
|
border: none;
|
||||||
|
margin-top: -22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-collapse-item__header {
|
||||||
|
border: none;
|
||||||
|
line-height: 25px;
|
||||||
|
height: 25px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-collapse-item__wrap {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-collapse-item__content {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding-bottom: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog >>> .el-timeline-item__node--01 {
|
||||||
|
width: 18px !important;
|
||||||
|
height: 18px !important;
|
||||||
|
left: 117px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.time-dialog .el-dialog {
|
||||||
|
margin: 0 auto !important;
|
||||||
|
max-height: 90%;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog .el-dialog {
|
||||||
|
margin: 0 auto !important;
|
||||||
|
height: 65%;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog .el-dialog__body {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 46px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px 20px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog .el-timeline-item__wrapper {
|
||||||
|
top: -20px !important;
|
||||||
|
}
|
||||||
|
.el-scrollbar__bar{
|
||||||
|
z-index:999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,58 +1,102 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="showDialog" :title="t('upgrade.title')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :before-close="dialogClose">
|
<el-dialog v-model="showDialog" :title="t('upgrade.title')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :before-close="dialogClose">
|
||||||
|
|
||||||
<div v-show="active == 'content'">
|
<template v-if="upgradeContent">
|
||||||
|
<!-- 检测服务是否到期 -->
|
||||||
<div class="h-[60vh] flex flex-col" v-if="upgradeContent">
|
<template v-if="step == 1">
|
||||||
|
<template v-for="(item, index) in upgradeContent.content">
|
||||||
<div class="text-lg">
|
<div class="text-lg">
|
||||||
本次升级将从<span class="font-bold">{{ upgradeContent.version }}</span>升级到<span class="font-bold">{{ upgradeContent.upgrade_version }}</span>版本
|
<template v-if="item.upgrade_version">
|
||||||
|
<span>【{{ item.app.app_name }}】本次升级将从</span>
|
||||||
|
<span class="font-bold px-[2px]">{{ item.version }}</span>
|
||||||
|
<span>升级到</span>
|
||||||
|
<span class="font-bold px-[2px]">{{ item.upgrade_version }}</span>
|
||||||
|
<span>版本</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="upgradeContent.content.length > 1">
|
||||||
|
<span>【{{ item.app.app_name }}】当前版本</span>
|
||||||
|
<span class="font-bold px-[2px]">{{ item.version }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span>当前版本</span>
|
||||||
|
<span class="font-bold px-[2px]">{{ item.version }}</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[10px]" v-if="upgradeContent.upgrade_version != upgradeContent.last_version">
|
<div class="mt-[10px]" :class="{ 'mb-[10px]' : (index + 1) < upgradeContent.content.length }" v-if="item.upgrade_version != item.last_version">
|
||||||
<el-alert type="info" show-icon>
|
<el-alert type="info" show-icon :closable="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
当前最新版本为{{ upgradeContent.last_version }},您的服务{{ upgradeContent.expire_time ? `已于${upgradeContent.expire_time}到期` : '长期有效' }}。如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级
|
<span>当前最新版本为{{ item.last_version }},您的服务{{ item.expire_time ? `已于${item.expire_time}到期` : '长期有效' }}。</span>
|
||||||
|
<span>如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级</span>
|
||||||
</template>
|
</template>
|
||||||
</el-alert>
|
</el-alert>
|
||||||
</div>
|
</div>
|
||||||
<el-scrollbar class="flex-1 h-0 mt-[20px]">
|
</template>
|
||||||
<div class="mt-[20px]" v-for="(item, index) in upgradeContent.version_list" :key="index">
|
</template>
|
||||||
<div class="font-bold text-lg">{{ item.version_no }}</div>
|
<div v-if="step == 2">
|
||||||
<div class="mt-[5px]" v-if="item.release_time">{{ item.release_time }}</div>
|
<el-steps :active="numberOfSteps" align-center class="number-of-steps" finish-status="success" process-status="process">
|
||||||
<div class="mt-[10px] p-[10px] rounded bg-[#f4f4f5] whitespace-pre-wrap !break-all" v-if="item.upgrade_log" v-html="item.upgrade_log"></div>
|
<el-step :title="t('testDirectoryPermissions')" />
|
||||||
</div>
|
<el-step :title="t('backupFiles')" />
|
||||||
</el-scrollbar>
|
<el-step :title="t('startUpgrade')" />
|
||||||
</div>
|
<el-step :title="t('upgradeEnd')" />
|
||||||
<div class="flex justify-end" v-if="upgradeContent.version_list.length">
|
</el-steps>
|
||||||
<el-button type="primary" @click="handleUpgrade" :loading="uploading">{{ t('upgrade.upgradeButton') }}</el-button>
|
<div class="h-[400px]" style="overflow: auto">
|
||||||
</div>
|
<!-- <div class="time-dialog-wrap mt-[30px]" v-show="active == 'content'">-->
|
||||||
</div>
|
<!-- <el-timeline style="width: 100%">-->
|
||||||
|
<!-- <el-timeline-item v-for="(item, index) in upgradeContent.version_list" :key="index" placement="left">-->
|
||||||
|
<!-- <div class="relative">-->
|
||||||
|
<!-- <span class="text-[#333333] text-[16px] absolute">{{ timeSplit(item.release_time)[0] }}</span>-->
|
||||||
|
<!-- <br />-->
|
||||||
|
<!-- <span class="text-[#999999] text-[14px] w-[78px] block mt-[10px] absolute" style="text-align: right"> {{ timeSplit(item.release_time)[1] }}</span>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <el-collapse v-model="activeName" accordion>-->
|
||||||
|
<!-- <el-collapse-item :name="index">-->
|
||||||
|
<!-- <template #title>-->
|
||||||
|
<!-- <span class="text-[#333] text-[16px]"> v{{ item.version_no }} </span>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
|
||||||
|
<!-- <div class="px-[20px] py-[20px] bg-overlay timeline-log-wrap whitespace-pre-wrap rounded-[4px]" style="background: rgba(25, 103, 249, 0.03);" v-if="item['upgrade_log']">-->
|
||||||
|
<!-- <div v-html="item['upgrade_log']"></div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </el-collapse-item>-->
|
||||||
|
<!-- </el-collapse>-->
|
||||||
|
<!-- </el-timeline-item>-->
|
||||||
|
<!-- </el-timeline>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<!-- 判断文件权限 -->
|
||||||
<div v-show="active == 'upgrade'">
|
<div v-show="active == 'upgrade'">
|
||||||
<div class="h-[60vh] flex flex-col" v-if="upgradeCheck && !upgradeTask">
|
<div class="flex flex-col" v-if="upgradeCheck && !upgradeTask">
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<div class="bg-[#fff] my-3" v-if="upgradeCheck.dir">
|
<div class="bg-[#fff] my-3" v-if="upgradeCheck.dir">
|
||||||
<p class="pt-[20px] pl-[20px] ">{{ t('upgrade.dirPermission') }}</p>
|
|
||||||
<div class="px-[20px] pt-[10px] text-[14px] el-table">
|
<div class="px-[20px] pt-[10px] text-[14px] el-table">
|
||||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<span>{{ t('upgrade.path') }}</span>
|
<span>{{ t("upgrade.path") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('upgrade.demand') }}</span>
|
<span>{{ t("upgrade.demand") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('status') }}</span>
|
<span>{{ t("status") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<div style="height: calc(300px); overflow: auto">
|
||||||
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_readable">
|
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_readable">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<span>{{ item.dir }}</span>
|
<span>{{ item.dir }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('upgrade.readable') }}</span>
|
<span>{{ t("upgrade.readable") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
<span v-if="item.status">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<el-icon color="red">
|
<el-icon color="red">
|
||||||
<CloseBold />
|
<CloseBold />
|
||||||
@ -65,10 +109,14 @@
|
|||||||
<span>{{ item.dir }}</span>
|
<span>{{ item.dir }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('upgrade.write') }}</span>
|
<span>{{ t("upgrade.write") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
<span v-if="item.status">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<el-icon color="red">
|
<el-icon color="red">
|
||||||
<CloseBold />
|
<CloseBold />
|
||||||
@ -78,34 +126,89 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-[60vh]" v-show="upgradeTask">
|
<div class="h-[370px] mt-[30px]" v-show="upgradeTask">
|
||||||
<terminal ref="terminalRef" :context="upgradeTask ? upgradeTask.upgrade.app_key : ''" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd" />
|
<terminal ref="terminalRef" :context="upgradeTask ? upgradeTask.upgrade.app_key : ''" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 是否备份选择 -->
|
||||||
|
<div class="flex flex-col" v-show="active == 'backup'">
|
||||||
|
<el-scrollbar>
|
||||||
|
<div class="bg-[#fff] my-3">
|
||||||
|
|
||||||
<div v-show="active == 'complete'">
|
<div class="px-[20px] pt-[10px] text-[14px] el-table" v-if="!upgradeContent.last_backup">
|
||||||
<div class="h-[60vh] flex flex-col">
|
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||||
<div class="flex-1 h-0">
|
<el-col :span="20">
|
||||||
|
<span>功能操作</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<span>状态</span>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row class="pb-[10px] items pl-[15px]" v-for="item in excludeSteps">
|
||||||
|
<el-col :span="20">
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<span>
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div class="pl-[50px] pt-[50px]" v-else>
|
||||||
|
<el-checkbox v-model="isNeedBackup" :label="t('upgrade.isNeedBackup')" :true-value="true" :false-value="false" size="large" >
|
||||||
|
</el-checkbox>
|
||||||
|
<div class="backup">{{ t('upgrade.isNeedBackupTips') }}<el-button link type="primary" @click="toBackupRecord">{{ t('upgrade.isNeedBackupBtn') }}</el-button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[50px]" v-show="active == 'complete'">
|
||||||
<el-result icon="success" :title="t('upgrade.upgradeSuccess')"></el-result>
|
<el-result icon="success" :title="t('upgrade.upgradeSuccess')"></el-result>
|
||||||
<el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" />
|
<el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" v-show="upgradeTask && upgradeTask.executed && !upgradeTask.executed.includes('cloudBuild')"/>
|
||||||
</div>
|
|
||||||
<div class="flex justify-end">
|
|
||||||
<el-button type="default" @click="showDialog = false">{{ t('upgrade.localBuild') }}</el-button>
|
|
||||||
<el-button type="primary" @click="handleCloudBuild">{{ t('upgrade.cloudBuild') }}</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<!-- 查看升级内容 -->
|
||||||
|
<el-button v-if="step == 1 && upgradeContent.content.length && isAllowUpgrade" @click="step = 2" type="primary">{{ t("upgrade.upgradeButton") }}</el-button>
|
||||||
|
|
||||||
|
<template v-if="step == 2">
|
||||||
|
<!-- <el-button v-if="active == 'content'" @click="showDialog = false">{{ t("return") }}</el-button>-->
|
||||||
|
<el-button type="primary" :disabled="!is_pass" v-if="active == 'upgrade' && !upgradeTask" @click="() => { active = 'backup'; numberOfSteps = 1 }">{{ t("nextStep") }}</el-button>
|
||||||
|
<el-button v-if="active == 'backup'" @click="() => { active = 'upgrade'; numberOfSteps = 1 } ">{{ t("prev") }}</el-button>
|
||||||
|
<el-button type="primary" v-if="active == 'backup'" :loading="loading" @click="() => { upgradeAddonFn() }">{{ t("nextStep") }}</el-button>
|
||||||
|
<el-button v-if="active == 'complete'" @click="showDialog = false">{{ t("complete") }}</el-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog v-model="upgradeTipsShowDialog" :title="t('warning')" width="500px" draggable>
|
<el-dialog v-model="upgradeTipsShowDialog" :title="t('warning')" width="500px" draggable>
|
||||||
<span v-html="t('upgrade.upgradeTips')"></span>
|
<span v-html="t('upgrade.upgradeTips')"></span>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<el-button @click="upgradeTipsConfirm(true)" type="primary">{{ t('upgrade.knownToKnow') }}</el-button>
|
<el-button @click="upgradeTipsConfirm(true)" type="primary">{{ t("upgrade.knownToKnow") }}</el-button>
|
||||||
<el-button @click="upgradeTipsConfirm()" type="primary" plain>{{ t('upgrade.upgradeButton') }}</el-button>
|
<el-button @click="handleUpgrade()" type="primary" plain :loading="readyLoading">{{ t("upgrade.upgradeButton") }}</el-button>
|
||||||
<el-button @click="upgradeTipsShowDialog = false">{{ t('cancel') }}</el-button>
|
<el-button @click="upgradeTipsShowDialog = false">{{ t("cancel") }}</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="cloudBuildErrorTipsShowDialog" :title="t('warning')" width="500px" draggable :show-close="false">
|
||||||
|
<span v-html="t('upgrade.cloudBuildErrorTips')"></span>
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<el-button @click="cloudBuildError('local')" type="primary">{{ t("upgrade.localBuild") }}</el-button>
|
||||||
|
<el-button @click="cloudBuildError('retry')" type="primary">{{ t("upgrade.cloudBuild") }}({{ retrySecond }}S)</el-button>
|
||||||
|
<el-button @click="cloudBuildError('rollback')" type="primary">{{ t("upgrade.rollback") }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@ -116,25 +219,51 @@ import { ref, h, watch } from 'vue'
|
|||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getVersions } from '@/app/api/auth'
|
import { getVersions } from '@/app/api/auth'
|
||||||
import { getFrameworkNewVersion } from '@/app/api/module'
|
import { getFrameworkNewVersion } from '@/app/api/module'
|
||||||
import { getUpgradeContent, getUpgradeTask, upgradeAddon, executeUpgrade, preUpgradeCheck, clearUpgradeTask } from '@/app/api/upgrade'
|
import {
|
||||||
|
getUpgradeContent,
|
||||||
|
getUpgradeTask,
|
||||||
|
upgradeAddon,
|
||||||
|
executeUpgrade,
|
||||||
|
preUpgradeCheck,
|
||||||
|
clearUpgradeTask, upgradeUserOperate
|
||||||
|
} from '@/app/api/upgrade'
|
||||||
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
||||||
import 'vue-web-terminal/lib/theme/dark.css'
|
import 'vue-web-terminal/lib/theme/dark.css'
|
||||||
import { AnyObject } from '@/types/global'
|
import { AnyObject } from '@/types/global'
|
||||||
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
|
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import Storage from '@/utils/storage'
|
import Storage from '@/utils/storage'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
const showDialog = ref<boolean>(false)
|
const showDialog = ref<boolean>(false)
|
||||||
const upgradeContent = ref<null | AnyObject>(null)
|
const upgradeContent = ref<null | AnyObject>(null)
|
||||||
|
const isAllowUpgrade = ref(true) // 是否允许升级
|
||||||
const upgradeTask = ref<null | AnyObject>(null)
|
const upgradeTask = ref<null | AnyObject>(null)
|
||||||
const active = ref('content')
|
const active = ref('upgrade')
|
||||||
|
const step = ref(1)
|
||||||
const upgradeCheck = ref<null | AnyObject>(null)
|
const upgradeCheck = ref<null | AnyObject>(null)
|
||||||
const uploading = ref(false)
|
const loading = ref(false)
|
||||||
const terminalRef: any = ref(null)
|
const terminalRef: any = ref(null)
|
||||||
const emits = defineEmits(['complete', 'cloudbuild'])
|
const emits = defineEmits(['complete', 'cloudbuild'])
|
||||||
const upgradeTipsShowDialog = ref<boolean>(false)
|
const upgradeTipsShowDialog = ref<boolean>(false)
|
||||||
|
|
||||||
let upgradeLog: any = []
|
let upgradeLog: any = []
|
||||||
let errorLog: any = []
|
let errorLog: any = []
|
||||||
|
const cloudBuildErrorTipsShowDialog = ref<boolean>(false)
|
||||||
|
const retrySecond = ref(30)
|
||||||
|
let retrySecondInterval: any = null
|
||||||
|
const isNeedBackup = ref(true)
|
||||||
|
|
||||||
|
// 升级步骤排除,backupCode 备份代码,backupSql 备份数据库
|
||||||
|
const excludeSteps: any = ref([
|
||||||
|
{
|
||||||
|
name: '备份源码',
|
||||||
|
code: 'backupCode'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '备份数据库',
|
||||||
|
code: 'backupSql'
|
||||||
|
}
|
||||||
|
])
|
||||||
/**
|
/**
|
||||||
* 查询升级任务
|
* 查询升级任务
|
||||||
*/
|
*/
|
||||||
@ -142,6 +271,24 @@ const getUpgradeTaskFn = () => {
|
|||||||
getUpgradeTask().then(({ data }) => {
|
getUpgradeTask().then(({ data }) => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
|
|
||||||
|
if (!upgradeContent.value) {
|
||||||
|
upgradeContent.value = data.upgrade_content
|
||||||
|
let upgradeCount = 0
|
||||||
|
let failUpgradeCount = 0
|
||||||
|
for (let i = 0; i < upgradeContent.value.content.length; i++) {
|
||||||
|
if (upgradeContent.value.content[i].version_list.length) {
|
||||||
|
upgradeCount++
|
||||||
|
} else {
|
||||||
|
failUpgradeCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (upgradeContent.value.content.length == upgradeCount) {
|
||||||
|
isAllowUpgrade.value = true
|
||||||
|
} else if (upgradeContent.value.content.length == failUpgradeCount) {
|
||||||
|
isAllowUpgrade.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 检测有没有正在进行中的升级任务
|
// 检测有没有正在进行中的升级任务
|
||||||
if (!showDialog.value) {
|
if (!showDialog.value) {
|
||||||
showElNotification()
|
showElNotification()
|
||||||
@ -151,15 +298,18 @@ const getUpgradeTaskFn = () => {
|
|||||||
terminalRef.value.execute('clear')
|
terminalRef.value.execute('clear')
|
||||||
terminalRef.value.execute('开始升级')
|
terminalRef.value.execute('开始升级')
|
||||||
}
|
}
|
||||||
data.log.forEach(item => {
|
|
||||||
|
upgradeTask.value = data
|
||||||
|
|
||||||
|
data.log.forEach((item) => {
|
||||||
if (!upgradeLog.includes(item)) {
|
if (!upgradeLog.includes(item)) {
|
||||||
terminalRef.value.pushMessage({content: `正在执行:${item}`})
|
terminalRef.value.pushMessage({ content: `${item}` })
|
||||||
upgradeLog.push(item)
|
upgradeLog.push(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// 安装失败
|
// 安装失败
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
data.error.forEach(item => {
|
data.error.forEach((item) => {
|
||||||
if (!errorLog.includes(item)) {
|
if (!errorLog.includes(item)) {
|
||||||
terminalRef.value.pushMessage({ content: item, class: 'error' })
|
terminalRef.value.pushMessage({ content: item, class: 'error' })
|
||||||
errorLog.push(item)
|
errorLog.push(item)
|
||||||
@ -168,26 +318,43 @@ const getUpgradeTaskFn = () => {
|
|||||||
}
|
}
|
||||||
// 恢复完毕
|
// 恢复完毕
|
||||||
if (data.step == 'restoreComplete') {
|
if (data.step == 'restoreComplete') {
|
||||||
|
flashInterval && clearInterval(flashInterval)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 升级完成
|
// 升级完成
|
||||||
if (data.step == 'upgradeComplete') {
|
if (data.step == 'upgradeComplete') {
|
||||||
active.value = 'complete'
|
active.value = 'complete'
|
||||||
|
numberOfSteps.value = 4
|
||||||
notificationEl && notificationEl.close()
|
notificationEl && notificationEl.close()
|
||||||
emits('complete')
|
emits('complete')
|
||||||
clearUpgradeTask()
|
clearUpgradeTask()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
upgradeTask.value = data
|
numberOfSteps.value = 2
|
||||||
|
active.value = 'upgrade'
|
||||||
executeUpgradeFn()
|
executeUpgradeFn()
|
||||||
}).catch()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpgradeTaskFn()
|
getUpgradeTaskFn()
|
||||||
|
|
||||||
const executeUpgradeFn = () => {
|
const executeUpgradeFn = () => {
|
||||||
executeUpgrade().then(() => {
|
executeUpgrade().then(() => {
|
||||||
getUpgradeTaskFn()
|
getUpgradeTaskFn()
|
||||||
}).catch()
|
}).catch((err) => {
|
||||||
|
if (err.message.indexOf('队列') != -1) {
|
||||||
|
retrySecond.value = 30
|
||||||
|
retrySecondInterval = setInterval(() => {
|
||||||
|
retrySecond.value--
|
||||||
|
if (retrySecond.value == 0) {
|
||||||
|
cloudBuildError('retry')
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
cloudBuildErrorTipsShowDialog.value = true
|
||||||
|
} else {
|
||||||
|
ElMessage({ message: err.message, type: 'error' })
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let notificationEl: any = null
|
let notificationEl: any = null
|
||||||
@ -198,10 +365,10 @@ const showElNotification = () => {
|
|||||||
notificationEl = ElNotification.success({
|
notificationEl = ElNotification.success({
|
||||||
title: t('warning'),
|
title: t('warning'),
|
||||||
dangerouslyUseHTMLString: true,
|
dangerouslyUseHTMLString: true,
|
||||||
message: h('div', {}, [
|
message: h('div', {}, [t('upgrade.upgradingTips'), h('span', {
|
||||||
t('upgrade.upgradingTips'),
|
class: 'text-primary cursor-pointer',
|
||||||
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('upgrade.clickView')])
|
onClick: elNotificationClick
|
||||||
]),
|
}, [t('upgrade.clickView')])]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
showClose: false
|
showClose: false
|
||||||
})
|
})
|
||||||
@ -209,16 +376,18 @@ const showElNotification = () => {
|
|||||||
|
|
||||||
const elNotificationClick = () => {
|
const elNotificationClick = () => {
|
||||||
showDialog.value = true
|
showDialog.value = true
|
||||||
active.value = 'upgrade'
|
|
||||||
getUpgradeTaskFn()
|
getUpgradeTaskFn()
|
||||||
|
step.value = 2
|
||||||
|
numberOfSteps.value = 3
|
||||||
|
active.value = 'upgrade'
|
||||||
notificationEl && notificationEl.close()
|
notificationEl && notificationEl.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
const frameworkVersion = ref('')
|
const frameworkVersion = ref('')
|
||||||
getVersions().then(res => {
|
getVersions().then((res) => {
|
||||||
frameworkVersion.value = res.data.version.version
|
frameworkVersion.value = res.data.version.version
|
||||||
})
|
})
|
||||||
const newFrameworkVersion = ref("")
|
const newFrameworkVersion = ref('')
|
||||||
getFrameworkNewVersion().then(({ data }) => {
|
getFrameworkNewVersion().then(({ data }) => {
|
||||||
newFrameworkVersion.value = data.last_version
|
newFrameworkVersion.value = data.last_version
|
||||||
})
|
})
|
||||||
@ -226,44 +395,90 @@ getFrameworkNewVersion().then(({ data }) => {
|
|||||||
/**
|
/**
|
||||||
* 执行升级
|
* 执行升级
|
||||||
*/
|
*/
|
||||||
const handleUpgrade = async () => {
|
const is_pass = ref(false)
|
||||||
if (uploading.value) return
|
const repeat = ref(false)
|
||||||
uploading.value = true
|
const readyLoading = ref(false)
|
||||||
|
|
||||||
const appKey = upgradeContent.value?.app.app_key != 'niucloud-admin' ? upgradeContent.value?.app.app_key : ''
|
const handleUpgrade = async () => {
|
||||||
|
if (repeat.value) return
|
||||||
|
repeat.value = true
|
||||||
|
readyLoading.value = true
|
||||||
|
|
||||||
|
const appKey = upgradeContent.value?.upgrade_apps.join(',') != 'niucloud-admin' ? upgradeContent.value?.upgrade_apps.join(',') : ''
|
||||||
|
|
||||||
await preUpgradeCheck(appKey).then(async ({ data }) => {
|
await preUpgradeCheck(appKey).then(async ({ data }) => {
|
||||||
if (data.is_pass) {
|
upgradeCheck.value = data
|
||||||
await upgradeAddon(appKey).then(() => {
|
is_pass.value = data.is_pass
|
||||||
|
active.value = 'upgrade'
|
||||||
|
!upgradeTask.value ? (numberOfSteps.value = 0) : numberOfSteps.value
|
||||||
|
upgradeTipsShowDialog.value = false
|
||||||
|
showDialog.value = true
|
||||||
|
repeat.value = false
|
||||||
|
readyLoading.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
repeat.value = false
|
||||||
|
readyLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const upgradeAddonFn = () => {
|
||||||
|
if (!is_pass.value) return
|
||||||
|
if (loading.value) return
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
const appKey = upgradeContent.value?.upgrade_apps.join(',') != 'niucloud-admin' ? upgradeContent.value?.upgrade_apps.join(',') : ''
|
||||||
|
|
||||||
|
upgradeAddon(appKey, { is_need_backup: isNeedBackup.value }).then(() => {
|
||||||
getUpgradeTaskFn()
|
getUpgradeTaskFn()
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
uploading.value = false
|
loading.value = false
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
upgradeCheck.value = data
|
|
||||||
}
|
|
||||||
}).catch()
|
|
||||||
|
|
||||||
if (uploading.value) active.value = 'upgrade'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const open = (addonKey: string = '') => {
|
const open = (addonKey: string = '', callback = null) => {
|
||||||
if (upgradeTask.value) {
|
if (upgradeTask.value) {
|
||||||
ElMessage({ message: '已有正在执行中的升级任务', type: 'error' })
|
ElMessage({ message: '已有正在执行中的升级任务', type: 'error' })
|
||||||
showDialog.value = true
|
showDialog.value = true
|
||||||
|
step.value = 2
|
||||||
|
numberOfSteps.value = 3
|
||||||
|
active.value = 'upgrade'
|
||||||
|
if (callback) callback()
|
||||||
} else {
|
} else {
|
||||||
if (addonKey && frameworkVersion.value != newFrameworkVersion.value) {
|
if (addonKey && frameworkVersion.value != newFrameworkVersion.value) {
|
||||||
ElMessage({ message: '存在新版本框架,请先升级框架', type: 'error' })
|
ElMessage({ message: "存在新版本框架,请先升级框架", type: "error" })
|
||||||
|
if (callback) callback()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (loading.value) return
|
||||||
|
loading.value = true
|
||||||
getUpgradeContent(addonKey).then(({ data }) => {
|
getUpgradeContent(addonKey).then(({ data }) => {
|
||||||
|
loading.value = false
|
||||||
upgradeContent.value = data
|
upgradeContent.value = data
|
||||||
|
let upgradeCount = 0
|
||||||
|
let failUpgradeCount = 0
|
||||||
|
for (let i = 0; i < upgradeContent.value.content.length; i++) {
|
||||||
|
if (upgradeContent.value.content[i].version_list.length) {
|
||||||
|
upgradeCount++
|
||||||
|
} else {
|
||||||
|
failUpgradeCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (upgradeContent.value.content.length == upgradeCount) {
|
||||||
|
isAllowUpgrade.value = true
|
||||||
|
} else if (upgradeContent.value.content.length == failUpgradeCount) {
|
||||||
|
isAllowUpgrade.value = false
|
||||||
|
}
|
||||||
|
|
||||||
if (Storage.get('upgradeTipsLock')) {
|
if (Storage.get('upgradeTipsLock')) {
|
||||||
showDialog.value = true
|
handleUpgrade()
|
||||||
} else {
|
} else {
|
||||||
upgradeTipsShowDialog.value = true
|
upgradeTipsShowDialog.value = true
|
||||||
}
|
}
|
||||||
}).catch()
|
if (callback) callback()
|
||||||
|
}).catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
if (callback) callback()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,10 +498,10 @@ const onExecCmd = (key, command, success, failed, name) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const makeIterator = (array: string[]) => {
|
const makeIterator = (array: string[]) => {
|
||||||
var nextIndex = 0
|
let nextIndex = 0
|
||||||
return {
|
return {
|
||||||
next () {
|
next () {
|
||||||
if ((nextIndex + 1) == array.length) {
|
if (nextIndex + 1 == array.length) {
|
||||||
nextIndex = 0
|
nextIndex = 0
|
||||||
}
|
}
|
||||||
return { value: array[nextIndex++] }
|
return { value: array[nextIndex++] }
|
||||||
@ -295,45 +510,71 @@ const makeIterator = (array: string[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dialogClose = (done: () => {}) => {
|
const dialogClose = (done: () => {}) => {
|
||||||
if (active.value == 'upgrade' && upgradeTask.value && !upgradeTask.value.error) {
|
if (active.value == 'upgrade' && upgradeTask.value && ['upgradeComplete', 'restoreComplete'].includes(upgradeTask.value.step) === false) {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(t('upgrade.showDialogCloseTips'), t('warning'), {
|
||||||
t('upgrade.showDialogCloseTips'),
|
|
||||||
t('warning'),
|
|
||||||
{
|
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}
|
}).then(() => {
|
||||||
).then(() => {
|
|
||||||
done()
|
done()
|
||||||
}).catch(() => { })
|
})
|
||||||
} else {
|
} else {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => showDialog.value, () => {
|
watch(
|
||||||
|
() => showDialog.value,
|
||||||
|
() => {
|
||||||
if (!showDialog.value) {
|
if (!showDialog.value) {
|
||||||
clearUpgradeTaskFn()
|
clearUpgradeTaskFn()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const clearUpgradeTaskFn = () => {
|
const clearUpgradeTaskFn = () => {
|
||||||
active.value = 'content'
|
active.value = 'upgrade'
|
||||||
uploading.value = false
|
loading.value = false
|
||||||
upgradeTask.value = null
|
upgradeTask.value = null
|
||||||
upgradeLog = []
|
upgradeLog = []
|
||||||
errorLog = []
|
errorLog = []
|
||||||
|
numberOfSteps.value = 0
|
||||||
flashInterval && clearInterval(flashInterval)
|
flashInterval && clearInterval(flashInterval)
|
||||||
clearUpgradeTask().then(() => {}).catch()
|
retrySecondInterval && clearInterval(retrySecondInterval)
|
||||||
|
isNeedBackup.value = true
|
||||||
|
step.value = 1
|
||||||
|
clearUpgradeTask().then(() => {
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 云编译
|
* 云编译队列不足操作
|
||||||
|
* @param event
|
||||||
*/
|
*/
|
||||||
const handleCloudBuild = () => {
|
const cloudBuildError = (event: string) => {
|
||||||
showDialog.value = false
|
cloudBuildErrorTipsShowDialog.value = false
|
||||||
emits('cloudbuild')
|
switch (event) {
|
||||||
|
case 'local':
|
||||||
|
upgradeUserOperate(event).then(() => {
|
||||||
|
getUpgradeTaskFn()
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'retry':
|
||||||
|
executeUpgradeFn()
|
||||||
|
retrySecondInterval && clearInterval(retrySecondInterval)
|
||||||
|
break
|
||||||
|
case 'rollback':
|
||||||
|
upgradeUserOperate(event).then(() => {
|
||||||
|
getUpgradeTaskFn()
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeSplit = (str: string) => {
|
||||||
|
const [date, time] = str.split(' ')
|
||||||
|
const [hours, minutes] = time.split(':')
|
||||||
|
return [date, `${hours}:${minutes}`]
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeTipsConfirm = (isLock: boolean = false) => {
|
const upgradeTipsConfirm = (isLock: boolean = false) => {
|
||||||
@ -341,9 +582,19 @@ const upgradeTipsConfirm = (isLock: boolean = false) => {
|
|||||||
upgradeTipsShowDialog.value = false
|
upgradeTipsShowDialog.value = false
|
||||||
!isLock && (showDialog.value = true)
|
!isLock && (showDialog.value = true)
|
||||||
}
|
}
|
||||||
|
const activeName = ref(0)
|
||||||
|
const numberOfSteps = ref(0)
|
||||||
|
|
||||||
|
const toBackupRecord = () => {
|
||||||
|
const routeUrl = router.resolve({
|
||||||
|
path: '/tools/backup_records'
|
||||||
|
})
|
||||||
|
window.open(routeUrl.href, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
open
|
open,
|
||||||
|
loading
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -351,7 +602,187 @@ defineExpose({
|
|||||||
.table-head-bg {
|
.table-head-bg {
|
||||||
background-color: var(--el-table-header-bg-color);
|
background-color: var(--el-table-header-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.terminal .t-log-box span) {
|
:deep(.terminal .t-log-box span) {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep .number-of-steps {
|
||||||
|
.el-step__line {
|
||||||
|
margin: 0 25px;
|
||||||
|
background: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-step__head {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-success {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
|
||||||
|
.el-step__icon {
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
|
||||||
|
box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-step__line {
|
||||||
|
margin: 0 25px;
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-process {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-weight: inherit;
|
||||||
|
|
||||||
|
// font-size: 18px;
|
||||||
|
.el-step__icon {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid var(--el-color-primary);
|
||||||
|
box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-wait {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-timeline-item {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-timeline-item >>> .el-timeline-item__node--normal {
|
||||||
|
left: 117px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
|
||||||
|
background: rgba(25, 103, 249, 0.12) !important;
|
||||||
|
border-radius: 50%;
|
||||||
|
/* 创建圆形 */
|
||||||
|
position: relative;
|
||||||
|
/* 用于定位伪元素 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-timeline-item >>> .el-timeline-item__node--normal::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 8px;
|
||||||
|
/* 中心圆直径 */
|
||||||
|
height: 8px;
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
/* 中心圆颜色 */
|
||||||
|
border-radius: 50%;
|
||||||
|
/* 中心圆为圆形 */
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
/* 居中显示 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-timeline-item >>> .el-timeline-item__tail {
|
||||||
|
left: 125px;
|
||||||
|
border-left-color: #dddddd;
|
||||||
|
border-left-style: dashed;
|
||||||
|
margin: 12px 0;
|
||||||
|
margin-top: 24px;
|
||||||
|
height: calc(100% - 24px - 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-dialog__header {
|
||||||
|
padding: 10px 20px;
|
||||||
|
height: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
text-align: left;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: solid 1px #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-card__body {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-card.is-always-shadow {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-dialog__headerbtn .el-dialog__close {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-dialog__headerbtn:hover .el-dialog__close {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-dialog__headerbtn {
|
||||||
|
top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-collapse {
|
||||||
|
margin-left: 119px;
|
||||||
|
border: none;
|
||||||
|
margin-top: -22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-collapse-item__header {
|
||||||
|
border: none;
|
||||||
|
line-height: 25px;
|
||||||
|
height: 25px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-collapse-item__wrap {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-collapse-item__content {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap >>> .el-timeline-item__node--01 {
|
||||||
|
width: 18px !important;
|
||||||
|
height: 18px !important;
|
||||||
|
left: 117px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.time-dialog-wrap .el-dialog {
|
||||||
|
margin: 0 auto !important;
|
||||||
|
max-height: 90%;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap .el-dialog {
|
||||||
|
margin: 0 auto !important;
|
||||||
|
height: 65%;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap .el-dialog__body {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 46px;
|
||||||
|
bottom: 0px;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px 20px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-dialog-wrap .el-timeline-item__wrapper {
|
||||||
|
top: -20px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -25,11 +25,9 @@ import { t } from '@/lang'
|
|||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import { deepClone } from '@/utils/common'
|
import { deepClone } from '@/utils/common'
|
||||||
import { getUserInfo, setUserInfo } from '@/app/api/personal'
|
import { getUserInfo, setUserInfo } from '@/app/api/personal'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const router = useRouter()
|
|
||||||
// 提交信息
|
// 提交信息
|
||||||
const saveInfo: any = reactive({})
|
const saveInfo: any = reactive({})
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"todayData": "today's data",
|
|
||||||
"memberNumb": "number of member",
|
|
||||||
"numberOfSites": "number of sites",
|
|
||||||
"numberOfVisitors": "number of visitors",
|
|
||||||
"commonlyUsedFunction": "commonly used function",
|
|
||||||
"articleList": "article list",
|
|
||||||
"memberManagement": "member management",
|
|
||||||
"balanceAccount": "balance account",
|
|
||||||
"administrator": "administrator",
|
|
||||||
"WebDecoration": "website decoration",
|
|
||||||
"accessMessage": "access message",
|
|
||||||
"memberDistribution": "membership distribution",
|
|
||||||
"systemInfo": "system environment",
|
|
||||||
"os": "os",
|
|
||||||
"phpVersions": "php version number",
|
|
||||||
"productionEnvironment": "production environment",
|
|
||||||
"versionsInfo": "version information",
|
|
||||||
"versions": "current version",
|
|
||||||
"frame": "framework based",
|
|
||||||
"channel": "access channel",
|
|
||||||
"serviceSupport": "service support",
|
|
||||||
"officialWbsite": "official website",
|
|
||||||
"pageView": "page view"
|
|
||||||
}
|
|
||||||
18
admin/src/app/lang/zh-cn/app.upgrade.json
Normal file
18
admin/src/app/lang/zh-cn/app.upgrade.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"companyName": "授权主体",
|
||||||
|
"siteAddress": "授权域名",
|
||||||
|
"contactName": "授权联系人",
|
||||||
|
"authCode": "授权码",
|
||||||
|
"authSecret": "授权秘钥",
|
||||||
|
"createTime": "授权时间",
|
||||||
|
"expireTime": "到期时间",
|
||||||
|
"authApp": "授权应用",
|
||||||
|
"authAppKey": "应用标识",
|
||||||
|
"siteAddressTips": "授权域名不匹配",
|
||||||
|
"authCodePlaceholder": "请输入授权码",
|
||||||
|
"authSecretPlaceholder": "请输入授权秘钥",
|
||||||
|
"updateCode": "重新绑定",
|
||||||
|
"notHaveAuth": "还没有授权?去购买",
|
||||||
|
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
|
||||||
|
"versionTips": "已经升级到最新版本"
|
||||||
|
}
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"cashOutInfo":"收款方信息",
|
"cashOutInfo":"收款方信息",
|
||||||
"transferCode":"收款码",
|
"transferCode":"收款码",
|
||||||
"realname":"真实姓名",
|
"realname":"真实姓名",
|
||||||
"account":"账号",
|
"account":"收款账号",
|
||||||
"bankRealname":"持卡人姓名",
|
"bankRealname":"持卡人姓名",
|
||||||
"remark":"备注",
|
"remark":"备注",
|
||||||
"remarkPlaceholder":"请输入备注",
|
"remarkPlaceholder":"请输入备注",
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
{
|
|
||||||
"dataSummarize": "数据概况",
|
|
||||||
"todayData": "今日数据",
|
|
||||||
"memberNumb": "新增会员数",
|
|
||||||
"orderMoney": "订单金额",
|
|
||||||
"numberOfVisitors": "今日访客数",
|
|
||||||
"commonlyUsedFunction": "常用功能",
|
|
||||||
"articleList": "文章列表",
|
|
||||||
"memberManagement": "会员管理",
|
|
||||||
"balanceAccount": "余额账户",
|
|
||||||
"administrator": "站点用户",
|
|
||||||
"WebDecoration": "网站装修",
|
|
||||||
"accessMessage": "访问消息",
|
|
||||||
"memberDistribution": "会员分布",
|
|
||||||
"systemInfo": "系统环境",
|
|
||||||
"os": "操作系统:",
|
|
||||||
"phpVersions": "PHP版本号:",
|
|
||||||
"productionEnvironment": "生产环境:",
|
|
||||||
"versionsInfo": "版本信息",
|
|
||||||
"versions": "当前版本",
|
|
||||||
"frame": "基于框架",
|
|
||||||
"channel": "获取渠道",
|
|
||||||
"serviceSupport": "官方客服",
|
|
||||||
"officialWbsite": "官网",
|
|
||||||
"pageView": "访问量",
|
|
||||||
"siteInfo":"站点信息",
|
|
||||||
"siteName":"站点名称",
|
|
||||||
"groupName":"站点套餐",
|
|
||||||
"expireTime":"过期时间",
|
|
||||||
"permanent":"永久",
|
|
||||||
"statusName":"站点状态",
|
|
||||||
"newSiteSum": "新增站点数",
|
|
||||||
"total": "总计",
|
|
||||||
"newMemberSum": "新增用户数",
|
|
||||||
"siteList": "站点列表",
|
|
||||||
"sitePackage": "站点套餐",
|
|
||||||
"newSite": "新增站点",
|
|
||||||
"appMarketplace": "应用市场",
|
|
||||||
"siteDistribution": "站点分布",
|
|
||||||
"addUser": "新增用户",
|
|
||||||
"normalSiteSum": "正常站点(个)",
|
|
||||||
"weekExpireSiteCount":"即将到期站点(个)",
|
|
||||||
"expireSiteSum": "过期站点(个)",
|
|
||||||
"noInstallAppSun": "未安装应用(个)",
|
|
||||||
"installAppSun": "已安装应用(个)",
|
|
||||||
"officialAccount": "Niucloud官方公众号",
|
|
||||||
"officialAccountDesc": "微信扫码关注",
|
|
||||||
"WeCom": "客服二维码",
|
|
||||||
"WeComDesc": "扫码联系客服",
|
|
||||||
"tel": "服务热线:",
|
|
||||||
"newVersion": "最新版本"
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
{
|
|
||||||
"todayData": "实时概况",
|
|
||||||
"memberNumb": "新增会员数(人)",
|
|
||||||
"orderMoney": "订单金额(元)",
|
|
||||||
"numberOfSites": "站点数量",
|
|
||||||
"numberOfVisitors": "今日访客数(人)",
|
|
||||||
"commonlyUsedFunction": "常用功能",
|
|
||||||
"articleList": "文章列表",
|
|
||||||
"memberManagement": "会员管理",
|
|
||||||
"balanceAccount": "余额账户",
|
|
||||||
"administrator": "管理员",
|
|
||||||
"WebDecoration": "网站装修",
|
|
||||||
"accessMessage": "访问消息",
|
|
||||||
"memberDistribution": "会员分布",
|
|
||||||
"systemInfo": "系统环境",
|
|
||||||
"os": "操作系统",
|
|
||||||
"phpVersions": "PHP版本号",
|
|
||||||
"productionEnvironment": "生产环境",
|
|
||||||
"versionsInfo": "版本信息",
|
|
||||||
"versions": "当前版本",
|
|
||||||
"frame": "基于框架",
|
|
||||||
"channel": "获取渠道",
|
|
||||||
"serviceSupport": "服务支持",
|
|
||||||
"officialWbsite": "官网",
|
|
||||||
"pageView": "访客数(人)",
|
|
||||||
"siteInfo":"站点信息",
|
|
||||||
"siteName":"站点名称",
|
|
||||||
"groupName":"站点套餐",
|
|
||||||
"expireTime":"过期时间",
|
|
||||||
"permanent":"永久",
|
|
||||||
"statusName":"站点状态",
|
|
||||||
"orderNumber": "订单数(笔)",
|
|
||||||
"wechatCode": "公众号二维码",
|
|
||||||
"wechatCodeDesc": "微信扫码关注",
|
|
||||||
"enterpriseWechatCode": "客服二维码",
|
|
||||||
"enterpriseWechatCodeDesc": "扫码联系客服",
|
|
||||||
"tel": "服务热线:",
|
|
||||||
"message": "请联系客服",
|
|
||||||
"messageTitle": "提示",
|
|
||||||
"accumulative":"累计",
|
|
||||||
"officialAccount": "Niucloud官方公众号",
|
|
||||||
"officialAccountDesc": "微信扫码关注",
|
|
||||||
"WeCom": "添加企业微信群",
|
|
||||||
"path": "地址",
|
|
||||||
"menuName": "名称",
|
|
||||||
"menuNamePlaceholder": "模版名称",
|
|
||||||
"menuBgColor": "背景颜色",
|
|
||||||
"menuImg": "选择图标",
|
|
||||||
"menuDesc": "描述",
|
|
||||||
"addShortcutMenu": "添加快捷模版",
|
|
||||||
"appTemplate": "应用模块",
|
|
||||||
"siteType": "站点类型",
|
|
||||||
"periodTime": "有效期",
|
|
||||||
"renew": "续费",
|
|
||||||
"selectModel": "选择模块",
|
|
||||||
"addMenu": "添加模块",
|
|
||||||
"shortcutLink": "模版",
|
|
||||||
"emptyMenu": "暂无快捷模块",
|
|
||||||
"select": "选择",
|
|
||||||
"custom": "自定义",
|
|
||||||
"accessSite": "访问站点",
|
|
||||||
"pathSelect": "选择模块",
|
|
||||||
"bgColorPlaceholder": "请选择背景色",
|
|
||||||
"iconPlaceholder": "请选择图标",
|
|
||||||
"pathPlaceholder": "请选择链接",
|
|
||||||
"descPlaceholder": "输入描述语…"
|
|
||||||
}
|
|
||||||
@ -43,6 +43,7 @@
|
|||||||
"versionCode": "版本号",
|
"versionCode": "版本号",
|
||||||
"createTime": "发布时间",
|
"createTime": "发布时间",
|
||||||
"buyLabel": "已购买",
|
"buyLabel": "已购买",
|
||||||
|
"recentlyUpdated": "最近更新",
|
||||||
"installTips": "安装后需手动更新插件引用的依赖和编译各个端口的前端源码",
|
"installTips": "安装后需手动更新插件引用的依赖和编译各个端口的前端源码",
|
||||||
"localInstall": "本地安装",
|
"localInstall": "本地安装",
|
||||||
"cloudInstall": "一键云安装",
|
"cloudInstall": "一键云安装",
|
||||||
@ -58,6 +59,7 @@
|
|||||||
"link": "官方应用市场",
|
"link": "官方应用市场",
|
||||||
"descriptionRight": "逛逛",
|
"descriptionRight": "逛逛",
|
||||||
"installed-empty": "暂未安装任何应用",
|
"installed-empty": "暂未安装任何应用",
|
||||||
|
"recentlyUpdatedEmpty": "暂无最近更新应用",
|
||||||
"siteAddressTips": "授权域名不匹配",
|
"siteAddressTips": "授权域名不匹配",
|
||||||
"authCodePlaceholder": "请输入授权码",
|
"authCodePlaceholder": "请输入授权码",
|
||||||
"authSecretPlaceholder": "请输入授权秘钥",
|
"authSecretPlaceholder": "请输入授权秘钥",
|
||||||
@ -68,9 +70,10 @@
|
|||||||
"appIdentification": "应用标识",
|
"appIdentification": "应用标识",
|
||||||
"tipText": "标识指开发应用或插件的文件夹名称",
|
"tipText": "标识指开发应用或插件的文件夹名称",
|
||||||
"uninstallTips": "是否要卸载该插件?",
|
"uninstallTips": "是否要卸载该插件?",
|
||||||
"upgrade": "升级",
|
"upgrade": "一键升级",
|
||||||
"newVersion": "最新版本",
|
"newVersion": "最新版本",
|
||||||
"cloudBuild": "云编译",
|
"cloudBuild": "云编译",
|
||||||
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
|
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
|
||||||
"deleteAddonTips": "删除插件会把插件目录连同文件全部删除,确定要删除吗?"
|
"deleteAddonTips": "删除插件会把插件目录连同文件全部删除,确定要删除吗?",
|
||||||
|
"batchUpgrade": "批量升级"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
"logging": "登录中",
|
"logging": "登录中",
|
||||||
"platform": "管理端",
|
"platform": "管理端",
|
||||||
"login" : "登录",
|
"login" : "登录",
|
||||||
"siteLogin": "站点登录",
|
|
||||||
"adminLogin": "平台登录",
|
"adminLogin": "平台登录",
|
||||||
"userPlaceholder": "请输入您的账号",
|
"userPlaceholder": "请输入您的账号",
|
||||||
"passwordPlaceholder": "请输入您的密码",
|
"passwordPlaceholder": "请输入您的密码",
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"continueSign": "连签天数",
|
"continueSign": "连签天数",
|
||||||
"continueSignFormatError": "连签天数格式错误",
|
"continueSignFormatError": "连签天数格式错误",
|
||||||
"continueSignBerweenDays": "连签天数为2-365天",
|
"continueSignBerweenDays": "连签天数为2-365天",
|
||||||
|
"continueSignMustLessThanSignPeriod": "连签天数不能大于签到周期",
|
||||||
"receiveLimit": "领取限制",
|
"receiveLimit": "领取限制",
|
||||||
"noLimit": "不限制",
|
"noLimit": "不限制",
|
||||||
"everyOneLimit": "每人限领",
|
"everyOneLimit": "每人限领",
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
"searchValueEmptyTips": "请输入搜索内容",
|
"searchValueEmptyTips": "请输入搜索内容",
|
||||||
"verify": "核销",
|
"verify": "核销",
|
||||||
"buyInfo": "预订信息",
|
"buyInfo": "预订信息",
|
||||||
"orderRefunding": "该订单正在维权中不能进行核销",
|
"orderRefunding": "该订单正在售后中不能进行核销",
|
||||||
"verifyTips": "是否要核销该订单?",
|
"verifyTips": "是否要核销该订单?",
|
||||||
"toOrder": "查看订单",
|
"toOrder": "查看订单",
|
||||||
"verifyType": "核销类型",
|
"verifyType": "核销类型",
|
||||||
@ -27,5 +27,5 @@
|
|||||||
"memberInfo": "会员信息",
|
"memberInfo": "会员信息",
|
||||||
"memberIdPlaceholder": "请选择会员",
|
"memberIdPlaceholder": "请选择会员",
|
||||||
"member": "会员",
|
"member": "会员",
|
||||||
"searchPlaceholder": "请输入会员昵称搜索"
|
"searchPlaceholder": "请输入会员编号/昵称/手机号"
|
||||||
}
|
}
|
||||||
@ -15,7 +15,7 @@
|
|||||||
"searchValueEmptyTips": "请输入搜索内容",
|
"searchValueEmptyTips": "请输入搜索内容",
|
||||||
"verify": "核销",
|
"verify": "核销",
|
||||||
"buyInfo": "预订信息",
|
"buyInfo": "预订信息",
|
||||||
"orderRefunding": "该订单正在维权中不能进行核销",
|
"orderRefunding": "该订单正在售后中不能进行核销",
|
||||||
"verifyTips": "是否要核销该订单?",
|
"verifyTips": "是否要核销该订单?",
|
||||||
"toOrder": "查看订单",
|
"toOrder": "查看订单",
|
||||||
"verifyType": "核销类型",
|
"verifyType": "核销类型",
|
||||||
|
|||||||
@ -3,6 +3,6 @@
|
|||||||
"type": "协议类型",
|
"type": "协议类型",
|
||||||
"titlePlaceholder": "请输入协议标题",
|
"titlePlaceholder": "请输入协议标题",
|
||||||
"contentPlaceholder": "请填写协议内容",
|
"contentPlaceholder": "请填写协议内容",
|
||||||
"contentMaxTips": "协议内容字符数应在5~100000之间",
|
"contentMaxTips": "协议内容字符数应在5~10000之间",
|
||||||
"content": "内容"
|
"content": "内容"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,8 @@
|
|||||||
"appPublicCertPathTips": "上传appCertPublicKey文件",
|
"appPublicCertPathTips": "上传appCertPublicKey文件",
|
||||||
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
|
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
|
||||||
"alipayRootCertPathTips": "上传alipayRootCert文件",
|
"alipayRootCertPathTips": "上传alipayRootCert文件",
|
||||||
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景"
|
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景",
|
||||||
|
"transferTips":"注意:应微信方规定,在2025年1月15日前开通商家转账到零钱服务的商户号可正常使用转账功能,之后开通的不支持使用转账到零钱服务",
|
||||||
|
"wechatpayPublicCert": "微信支付公钥",
|
||||||
|
"wechatpayPublicCertId": "微信支付公钥ID"
|
||||||
}
|
}
|
||||||
|
|||||||
28
admin/src/app/lang/zh-cn/tools.backup_records.json
Normal file
28
admin/src/app/lang/zh-cn/tools.backup_records.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"manualBackup": "手动备份",
|
||||||
|
"manualBackupTips": "即将开始手动备份您的源码和数据库,为确保备份过程中顺利进行以及数据的完整性,建议您暂停所有相关操作,避免因数据写入或修改导致备份不一致,是否确定继续?",
|
||||||
|
"id": "编号",
|
||||||
|
"content": "内容",
|
||||||
|
"currentVersion": "备份版本",
|
||||||
|
"contentPlaceholder": "请输入内容",
|
||||||
|
"batchDelete": "批量删除",
|
||||||
|
"backupDir": "备份路径",
|
||||||
|
"completeTime": "备份时间",
|
||||||
|
"restore": "恢复",
|
||||||
|
"restoreTips": "此操作将恢复备份的源码和数据库,并且覆盖当前数据。为避免意外损失,请确认已了解此操作的影响,并确保已备份重要信息,是否确定继续?",
|
||||||
|
"deleteTips": "删除记录将会同步删除其备份文件,确定要操作吗?",
|
||||||
|
"batchEmptySelectedTips": "请选择需要批量删除的记录",
|
||||||
|
"restoreTitle": "恢复备份",
|
||||||
|
"backupCompleteTips": "备份成功",
|
||||||
|
"restoreCompleteTips": "恢复成功",
|
||||||
|
"showDialogCloseTips": "任务尚未完成,关闭将会造成数据丢失或系统损坏的影响,是否要继续关闭?",
|
||||||
|
"manualBackupTitle": "手动备份",
|
||||||
|
"checkDirectoryPermissions": "检测目录权限",
|
||||||
|
"backupFiles": "备份文件",
|
||||||
|
"startUpgrade": "开始恢复",
|
||||||
|
"upgradeEnd": "恢复完成",
|
||||||
|
"startBackUp": "开始备份",
|
||||||
|
"backUpEnd": "备份完成",
|
||||||
|
"remark": "备注",
|
||||||
|
"remarkEmpty": "无"
|
||||||
|
}
|
||||||
12
admin/src/app/lang/zh-cn/tools.upgrade_records.json
Normal file
12
admin/src/app/lang/zh-cn/tools.upgrade_records.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"id": "编号",
|
||||||
|
"upgradeName": "内容",
|
||||||
|
"prevVersion": "前一版本",
|
||||||
|
"currentVersion": "版本",
|
||||||
|
"upgradeNamePlaceholder": "请输入内容",
|
||||||
|
"completeTime": "升级时间",
|
||||||
|
"status": "状态",
|
||||||
|
"failReason": "失败原因",
|
||||||
|
"batchDelete": "批量删除",
|
||||||
|
"deleteTips": "确定要删除吗?"
|
||||||
|
}
|
||||||
@ -2,47 +2,31 @@
|
|||||||
<!--授权信息-->
|
<!--授权信息-->
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<el-card class="box-card !border-none" shadow="never" v-if="!loading">
|
<el-card class="box-card !border-none" shadow="never" v-if="!loading">
|
||||||
|
<div>
|
||||||
<div class="flex">
|
<div class="text-[#333] text-[18px]">授权信息</div>
|
||||||
<div class="w-[450px] mr-[20px] p-[50px] bg-[var(--el-color-info-light-9)]">
|
<div class="ml-[50px] mt-[40px]">
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<span class="text-page-title">版本信息</span>
|
|
||||||
<div class="flex-1 w-0 flex justify-end">
|
|
||||||
<el-button class="text-[#4C4C4C] w-[78px] h-[32px] !bg-transparent" @click="getFrameworkVersionListFn" v-if="!newVersion || (newVersion && newVersion.version_no == versions)">检测更新</el-button>
|
|
||||||
<el-button class="text-[#4C4C4C] w-[78px] h-[32px]" type="primary" @click="handleUpgrade" v-else>一键升级</el-button>
|
|
||||||
<el-button class="text-[#4C4C4C] w-[78px] h-[32px]" type="primary" @click="handleCloudBuild" :loading="cloudBuildRef?.loading">云编译</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mt-[30px] flex items-center text-[14px] text-[#797979]">
|
|
||||||
<span>当前版本</span>
|
|
||||||
<span class="text-[26px] ml-[15px] mr-[10px] text-[#656668]">{{versions}}</span>
|
|
||||||
<em class="text-[12px]" v-if="!newVersion || (newVersion && newVersion.version_no == versions)">(当前已是最新版本)</em>
|
|
||||||
<em class="text-[12px] text-[red]" v-else>(最新版本{{ newVersion.version_no }})</em>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-1 justify-between items-center p-[50px] bg-[var(--el-color-info-light-9)]">
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex flex-wrap items-center">
|
<div class="flex flex-wrap items-center">
|
||||||
<p class="text-page-title mr-[20px]">授权信息</p>
|
<span class="mr-[6px] text-[14px] text-[#666666] w-[70px] text-right">授权公司:</span>
|
||||||
<span class="text-[14px] text-[#666]">{{ authinfo.company_name || '--' }}</span>
|
<span class="text-[14px] text-[#333]">{{ authinfo.company_name || "--" }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[46px] ml-[40px] flex flex-wrap">
|
<div class="flex flex-wrap items-center mt-[20px]">
|
||||||
<span class="text-[14px] mr-[84px]">授权域名<em class="ml-[12px] text-[12px]">{{ authinfo.site_address || '--' }}</em></span>
|
<span class="mr-[6px] text-[14px] text-[#666666] w-[70px] text-right">授权域名:</span>
|
||||||
<span class="text-[14px] flex items-center">
|
<span class="text-[14px] text-[#333]">{{ authinfo.site_address || "--" }}</span>
|
||||||
<span>授权码</span>
|
</div>
|
||||||
<em class="ml-[12px] mr-[10px] text-[12px]">{{ authinfo.auth_code ? (isCheck ? authinfo.auth_code : hideAuthCode(authinfo.auth_code)) : '--' }}</em>
|
<div class="flex flex-wrap items-center mt-[20px]">
|
||||||
<el-icon v-if="!isCheck" @click="isCheck = !isCheck" class="text-[12px] cursor-pointer">
|
<span class="mr-[6px] text-[14px] text-[#666666] w-[70px] text-right">授权码:</span>
|
||||||
|
<span class="text-[14px] text-[#333]">
|
||||||
|
<span class="mr-[10px]">{{ authinfo.auth_code ? (isCheck ? authinfo.auth_code : hideAuthCode(authinfo.auth_code)) : "--" }}</span>
|
||||||
|
<el-icon v-if="!isCheck" @click="isCheck = !isCheck" class="text-[12px] cursor-pointer text-[#4383F9]">
|
||||||
<View />
|
<View />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<el-icon v-else @click="isCheck = !isCheck" class="text-[12px] cursor-pointer">
|
<el-icon v-else @click="isCheck = !isCheck" class="text-[12px] cursor-pointer text-[#4383F9]"> <Hide /> </el-icon>
|
||||||
<Hide />
|
|
||||||
</el-icon>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-1 flex-wrap justify-end relative">
|
<div class="mt-[60px] mb-[50px]">
|
||||||
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
|
<el-button class="w-[150px] !h-[46px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
|
||||||
<el-popover ref="getAuthCodeDialog" placement="bottom-start" :width="478" trigger="click" class="mt-[8px]">
|
<el-popover ref="getAuthCodeDialog" placement="bottom-start" :width="478" trigger="click" class="mt-[8px]">
|
||||||
<div class="px-[18px] py-[8px]">
|
<div class="px-[18px] py-[8px]">
|
||||||
<p class="leading-[32px] text-[14px]">您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
<p class="leading-[32px] text-[14px]">您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
||||||
@ -52,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button class="w-[154px] !h-[48px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent" plain type="primary">如何获取授权码?</el-button>
|
<el-button class="w-[150px] !h-[46px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent" plain type="primary">如何获取授权码?</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
@ -70,13 +54,13 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-sm mt-[10px] text-info">{{ t('authInfoTips') }}</div>
|
<div class="text-sm mt-[10px] text-info">{{ t("authInfoTips") }}</div>
|
||||||
|
|
||||||
<div class="mt-[20px]">
|
<div class="mt-[20px]">
|
||||||
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t('confirm') }}</el-button>
|
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t("confirm") }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[10px] text-right">
|
<div class="mt-[10px] text-right">
|
||||||
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
|
<el-button type="primary" link @click="market">{{ t("notHaveAuth") }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -84,73 +68,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card class="box-card !border-none " shadow="never" v-if="!loading">
|
|
||||||
<div class="text-page-title mb-[20px]">历史版本</div>
|
|
||||||
<el-timeline>
|
|
||||||
<el-timeline-item :timestamp="item['release_time'] + ' 版本:' + item['version_no']" v-for="(item,index) in frameworkVersionList" type="primary" :hollow="true" placement="top" :key="index">
|
|
||||||
<div class="mt-[10px] p-[20px] bg-overlay rounded-md timeline-log-wrap whitespace-pre-wrap" v-if="item['upgrade_log']">
|
|
||||||
<div v-html="item['upgrade_log']"></div>
|
|
||||||
</div>
|
|
||||||
</el-timeline-item>
|
|
||||||
</el-timeline>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<upgrade ref="upgradeRef" />
|
|
||||||
<cloud-build ref="cloudBuildRef" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, computed } from 'vue'
|
import { reactive, ref } from "vue"
|
||||||
import { t } from '@/lang'
|
import { t } from "@/lang"
|
||||||
import { getVersions } from '@/app/api/auth'
|
import { getVersions } from "@/app/api/auth"
|
||||||
import { getInstallConfig } from '@/app/api/sys'
|
import { getAuthInfo, setAuthInfo } from "@/app/api/module"
|
||||||
import { getAuthInfo, setAuthInfo, getFrameworkVersionList } from '@/app/api/module'
|
import { FormInstance, FormRules } from "element-plus"
|
||||||
import { ElMessageBox, FormInstance, FormRules, ElMessage } from 'element-plus'
|
import { cloneDeep } from "lodash-es"
|
||||||
import Upgrade from '@/app/components/upgrade/index.vue'
|
|
||||||
import CloudBuild from '@/app/components/cloud-build/index.vue'
|
|
||||||
import { cloneDeep } from 'lodash-es'
|
|
||||||
|
|
||||||
const upgradeRef = ref<any>(null)
|
|
||||||
const cloudBuildRef = ref<any>(null)
|
|
||||||
const getAuthCodeDialog: Record<string, any> | null = ref(null)
|
const getAuthCodeDialog: Record<string, any> | null = ref(null)
|
||||||
|
|
||||||
const authCodeApproveDialog = ref(false)
|
const authCodeApproveDialog = ref(false)
|
||||||
const isCheck = ref(false)
|
const isCheck = ref(false)
|
||||||
const frameworkVersionList = ref([])
|
|
||||||
|
|
||||||
const installPhpConfig = ref(null)
|
|
||||||
|
|
||||||
getInstallConfig().then(({ data }) => {
|
|
||||||
installPhpConfig.value = data
|
|
||||||
}).catch()
|
|
||||||
|
|
||||||
const checkVersion = ref(false)
|
|
||||||
const getFrameworkVersionListFn = () => {
|
|
||||||
getFrameworkVersionList().then(({ data }) => {
|
|
||||||
frameworkVersionList.value = data
|
|
||||||
if (checkVersion.value) {
|
|
||||||
if (!newVersion.value || (newVersion.value && newVersion.value.version_no == versions.value)) {
|
|
||||||
ElMessage({
|
|
||||||
message: t('versionTips'),
|
|
||||||
type: 'success'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
checkVersion.value = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
getFrameworkVersionListFn()
|
|
||||||
|
|
||||||
const newVersion:any = computed(() => {
|
|
||||||
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
|
|
||||||
})
|
|
||||||
|
|
||||||
const hideAuthCode = (res: any) => {
|
const hideAuthCode = (res: any) => {
|
||||||
const authCode = cloneDeep(res)
|
const authCode = cloneDeep(res)
|
||||||
const data = authCode.slice(0, authCode.length / 2) + authCode.slice(authCode.length / 2, authCode.length - 1).replace(/./g, '*')
|
const data = authCode.slice(0, authCode.length / 2) + authCode.slice(authCode.length / 2, authCode.length - 1).replace(/./g, "*")
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,15 +94,15 @@ const authCodeApproveFn = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface AuthInfo {
|
interface AuthInfo {
|
||||||
company_name: string,
|
company_name: string
|
||||||
site_address: string,
|
site_address: string
|
||||||
auth_code: string
|
auth_code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const authinfo = ref<AuthInfo>({
|
const authinfo = ref<AuthInfo>({
|
||||||
company_name: '',
|
company_name: "",
|
||||||
site_address: '',
|
site_address: "",
|
||||||
auth_code: ''
|
auth_code: ""
|
||||||
})
|
})
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const saveLoading = ref(false)
|
const saveLoading = ref(false)
|
||||||
@ -187,19 +122,15 @@ const checkAppMange = () => {
|
|||||||
checkAppMange()
|
checkAppMange()
|
||||||
|
|
||||||
const formData = reactive<Record<string, string>>({
|
const formData = reactive<Record<string, string>>({
|
||||||
auth_code: '',
|
auth_code: "",
|
||||||
auth_secret: ''
|
auth_secret: ""
|
||||||
})
|
})
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const formRules = reactive<FormRules>({
|
const formRules = reactive<FormRules>({
|
||||||
auth_code: [
|
auth_code: [{ required: true, message: t("authCodePlaceholder"), trigger: "blur" }],
|
||||||
{ required: true, message: t('authCodePlaceholder'), trigger: 'blur' }
|
auth_secret: [{ required: true, message: t("authSecretPlaceholder"), trigger: "blur" }]
|
||||||
],
|
|
||||||
auth_secret: [
|
|
||||||
{ required: true, message: t('authSecretPlaceholder'), trigger: 'blur' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const save = async(formEl: FormInstance | undefined) => {
|
const save = async(formEl: FormInstance | undefined) => {
|
||||||
@ -221,47 +152,16 @@ const save = async (formEl: FormInstance | undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const market = () => {
|
const market = () => {
|
||||||
window.open(installPhpConfig.value?.website_url)
|
window.open("https://www.niucloud.com/app")
|
||||||
}
|
}
|
||||||
|
|
||||||
const versions = ref('')
|
const versions = ref("")
|
||||||
const getVersionsInfo = () => {
|
const getVersionsInfo = () => {
|
||||||
getVersions().then(res => {
|
getVersions().then((res) => {
|
||||||
versions.value = res.data.version.version
|
versions.value = res.data.version.version
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
getVersionsInfo()
|
getVersionsInfo()
|
||||||
|
|
||||||
/**
|
|
||||||
* 升级
|
|
||||||
*/
|
|
||||||
const handleUpgrade = () => {
|
|
||||||
if (!authinfo.value.auth_code) {
|
|
||||||
authCodeApproveFn()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
upgradeRef.value?.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCloudBuild = () => {
|
|
||||||
if (!authinfo.value.auth_code) {
|
|
||||||
authCodeApproveFn()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (cloudBuildRef.value.cloudBuildTask) {
|
|
||||||
cloudBuildRef.value?.open()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'),
|
|
||||||
{
|
|
||||||
confirmButtonText: t('confirm'),
|
|
||||||
cancelButtonText: t('cancel'),
|
|
||||||
type: 'warning'
|
|
||||||
}
|
|
||||||
).then(() => {
|
|
||||||
cloudBuildRef.value?.open()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -59,10 +59,6 @@ const appList = ref<Record<string, any>[]>([])
|
|||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const getAppList = async () => {
|
const getAppList = async () => {
|
||||||
// const res = await getSiteAddons()
|
|
||||||
// appList.value = res.data
|
|
||||||
// loading.value = false
|
|
||||||
|
|
||||||
const res = await getShowApp()
|
const res = await getShowApp()
|
||||||
appList.value = res.data
|
appList.value = res.data
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@ -70,7 +66,6 @@ const getAppList = async () => {
|
|||||||
getAppList()
|
getAppList()
|
||||||
|
|
||||||
const toLink = (item: any) => {
|
const toLink = (item: any) => {
|
||||||
console.log('tol', item)
|
|
||||||
if (item.url) {
|
if (item.url) {
|
||||||
router.push(item.url)
|
router.push(item.url)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
195
admin/src/app/views/app/upgrade.vue
Normal file
195
admin/src/app/views/app/upgrade.vue
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<template>
|
||||||
|
<!--授权信息-->
|
||||||
|
<div class="main-container">
|
||||||
|
<el-card class="box-card !border-none" shadow="never" v-if="newVersion">
|
||||||
|
<div>
|
||||||
|
<div class="mx-[20px] my-[20px]">
|
||||||
|
<div class="title text-[18px]">版本信息</div>
|
||||||
|
<div class="text-[18px] text-center mb-[7px] mt-[40px]">系统当前版本:v{{ version }}({{ versionCode }})</div>
|
||||||
|
<div class="text-center text-[#666] text-[14px]" v-if="!newVersion || (newVersion && newVersion.version_no == version)">
|
||||||
|
<span>当前已是最新版本,无需升级</span>
|
||||||
|
<span class="text-[14px] text-primary ml-[10px] cursor-pointer" @click="openUpgrade">更新说明</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-[#666] text-[14px] text-center" v-else>
|
||||||
|
当前系统最新版本为 <span class="text-[18px] text-[#FF4D01]">v{{ newVersion.version_no }}</span>
|
||||||
|
<span class="text-[14px] text-primary ml-[10px]" style="cursor: pointer" @click="openUpgrade">更新说明</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[30px] flex justify-center items-center">
|
||||||
|
<el-button class="text-[#4C4C4C] w-[150px] !h-[44px]" type="primary" :loading="loading" @click="handleUpgrade" v-if="!(!newVersion || (newVersion && newVersion.version_no == version))">一键升级</el-button>
|
||||||
|
<el-button class="text-[#4C4C4C] w-[130px] !h-[44px]" @click="upgradeRecord">升级记录</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
|
||||||
|
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
|
||||||
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
<el-form-item prop="auth_code">
|
||||||
|
<el-input v-model.trim="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width" clearable size="large" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<div class="mt-[20px]">
|
||||||
|
<el-form-item prop="auth_secret">
|
||||||
|
<el-input v-model.trim="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-sm mt-[10px] text-info">{{ t("authInfoTips") }}</div>
|
||||||
|
|
||||||
|
<div class="mt-[20px]">
|
||||||
|
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t("confirm") }}</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[10px] text-right">
|
||||||
|
<el-button type="primary" link @click="market">{{ t("notHaveAuth") }}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<upgrade ref="upgradeRef" />
|
||||||
|
<upgrade-log ref="upgradeLogRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, reactive } from "vue"
|
||||||
|
import { t } from "@/lang"
|
||||||
|
import { getVersions } from "@/app/api/auth"
|
||||||
|
import { getAuthInfo, getFrameworkVersionList, setAuthInfo } from "@/app/api/module"
|
||||||
|
import { ElMessage, FormInstance, FormRules } from "element-plus"
|
||||||
|
import { useRouter } from "vue-router"
|
||||||
|
import Upgrade from "@/app/components/upgrade/index.vue"
|
||||||
|
import UpgradeLog from "@/app/components/upgrade-log/index.vue"
|
||||||
|
|
||||||
|
const upgradeRef = ref<any>(null)
|
||||||
|
const upgradeLogRef = ref<any>(null)
|
||||||
|
const authCodeApproveDialog = ref(false)
|
||||||
|
const frameworkVersionList = ref([])
|
||||||
|
|
||||||
|
const checkVersion = ref(false)
|
||||||
|
|
||||||
|
const formData = reactive<Record<string, string>>({
|
||||||
|
auth_code: '',
|
||||||
|
auth_secret: ''
|
||||||
|
})
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const formRules = reactive<FormRules>({
|
||||||
|
auth_code: [{ required: true, message: t('authCodePlaceholder'), trigger: 'blur' }],
|
||||||
|
auth_secret: [{ required: true, message: t('authSecretPlaceholder'), trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
|
const saveLoading = ref(false)
|
||||||
|
|
||||||
|
const save = async(formEl: FormInstance | undefined) => {
|
||||||
|
if (saveLoading.value || !formEl) return
|
||||||
|
|
||||||
|
await formEl.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
saveLoading.value = true
|
||||||
|
|
||||||
|
setAuthInfo(formData).then(() => {
|
||||||
|
saveLoading.value = false
|
||||||
|
checkAppMange()
|
||||||
|
}).catch(() => {
|
||||||
|
saveLoading.value = false
|
||||||
|
authCodeApproveDialog.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFrameworkVersionListFn = () => {
|
||||||
|
getFrameworkVersionList().then(({ data }) => {
|
||||||
|
frameworkVersionList.value = data
|
||||||
|
if (checkVersion.value) {
|
||||||
|
if (!newVersion.value || (newVersion.value && newVersion.value.version_no == version.value)) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('versionTips'),
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
checkVersion.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
getFrameworkVersionListFn()
|
||||||
|
|
||||||
|
const newVersion: any = computed(() => {
|
||||||
|
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
|
||||||
|
})
|
||||||
|
|
||||||
|
const authCodeApproveFn = () => {
|
||||||
|
authCodeApproveDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const version = ref('')
|
||||||
|
const versionCode = ref('')
|
||||||
|
|
||||||
|
const getVersionsInfo = () => {
|
||||||
|
getVersions().then((res) => {
|
||||||
|
version.value = res.data.version.version
|
||||||
|
versionCode.value = res.data.version.code
|
||||||
|
})
|
||||||
|
}
|
||||||
|
getVersionsInfo()
|
||||||
|
|
||||||
|
interface AuthInfo {
|
||||||
|
company_name: string
|
||||||
|
site_address: string
|
||||||
|
auth_code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const authInfo = ref<AuthInfo>({
|
||||||
|
company_name: '',
|
||||||
|
site_address: '',
|
||||||
|
auth_code: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const repeat = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升级
|
||||||
|
*/
|
||||||
|
const handleUpgrade = () => {
|
||||||
|
if (!authInfo.value.auth_code) {
|
||||||
|
authCodeApproveFn()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (repeat.value) return
|
||||||
|
repeat.value = true
|
||||||
|
loading.value = true
|
||||||
|
upgradeRef.value?.open('', () => {
|
||||||
|
repeat.value = false
|
||||||
|
loading.value = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkAppMange = () => {
|
||||||
|
getAuthInfo().then((res) => {
|
||||||
|
if (res.data.data && res.data.data.length != 0) {
|
||||||
|
authInfo.value = res.data.data
|
||||||
|
authCodeApproveDialog.value = false
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
authCodeApproveDialog.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAppMange()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const upgradeRecord = () => {
|
||||||
|
router.push('/admin/tools/upgrade_records')
|
||||||
|
}
|
||||||
|
|
||||||
|
const openUpgrade = () => {
|
||||||
|
upgradeLogRef.value?.open()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<!--站点菜单-->
|
<!--平台菜单-->
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<el-card class="box-card !border-none" shadow="never">
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
|
||||||
|
|||||||
@ -102,7 +102,6 @@ const router = useRouter()
|
|||||||
const pageName = route.meta.title
|
const pageName = route.meta.title
|
||||||
|
|
||||||
const activeName = ref('/channel/aliapp')
|
const activeName = ref('/channel/aliapp')
|
||||||
const active = ref(2)
|
|
||||||
const qrCode = ref<string>('')
|
const qrCode = ref<string>('')
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const res = await getAliappConfig()
|
const res = await getAliappConfig()
|
||||||
|
|||||||
@ -24,11 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="py-[20px] px-[30px] h-[350px]">
|
<div class="py-[20px] px-[30px] h-[350px]">
|
||||||
<div v-if="formData.msgtype == 'text'">
|
<div v-if="formData.msgtype == 'text'">
|
||||||
<el-input
|
<el-input v-model.trim="formData.text.content" :rows="5" type="textarea" placeholder="" maxlength="600" :show-word-limit="true" resize="none" input-style="box-shadow: none;height:300px" />
|
||||||
v-model.trim="formData.text.content" :rows="5" type="textarea" placeholder="" maxlength="600" :show-word-limit="true"
|
|
||||||
resize="none"
|
|
||||||
input-style="box-shadow: none;height:300px"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="formData.msgtype == 'image'" class="flex w-full h-full justify-center items-center image-media">
|
<div v-if="formData.msgtype == 'image'" class="flex w-full h-full justify-center items-center image-media">
|
||||||
<div class="w-full h-full" v-if="formData.image.url">
|
<div class="w-full h-full" v-if="formData.image.url">
|
||||||
|
|||||||
@ -14,7 +14,9 @@
|
|||||||
</upload-media>
|
</upload-media>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex" v-else>
|
<div class="flex" v-else>
|
||||||
<el-button type="primary" :loading="syncLoading" @click="syncWechatNews">{{ syncLoading ? '同步中' : '同步微信图文'}}</el-button>
|
<el-button type="primary" :loading="syncLoading" @click="syncWechatNews">
|
||||||
|
{{ syncLoading ? '同步中' : '同步微信图文' }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -23,8 +25,10 @@
|
|||||||
<!-- 素材管理 -->
|
<!-- 素材管理 -->
|
||||||
<div v-if="attachment.data.length">
|
<div v-if="attachment.data.length">
|
||||||
<div class="flex flex-wrap" v-if="prop.type != 'news'">
|
<div class="flex flex-wrap" v-if="prop.type != 'news'">
|
||||||
<div class="attachment-item mr-[10px] mb-[10px] w-[120px]" v-for="(item, index) in attachment.data" :key="index" @click="selectedFile = item">
|
<div class="attachment-item mr-[10px] mb-[10px] w-[120px]"
|
||||||
<div class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
|
v-for="(item, index) in attachment.data" :key="index" @click="selectedFile = item">
|
||||||
|
<div
|
||||||
|
class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
|
||||||
<el-image :src="img(item.value)" fit="contain" v-if="type == 'image'" :preview-src-list="item.image_list" />
|
<el-image :src="img(item.value)" fit="contain" v-if="type == 'image'" :preview-src-list="item.image_list" />
|
||||||
<video :src="img(item.value)" v-else-if="type == 'video'"></video>
|
<video :src="img(item.value)" v-else-if="type == 'video'"></video>
|
||||||
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
||||||
@ -34,7 +38,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative" ref="waterfallContainerRef" v-else>
|
<div class="relative" ref="waterfallContainerRef" v-else>
|
||||||
<div ref="waterfallItemRef" class="absolute attachment-item mr-[10px] mb-[10px] w-[280px] rounded-lg overflow-hidden border border-color" v-for="(item, index) in attachment.data"
|
<div ref="waterfallItemRef"
|
||||||
|
class="absolute attachment-item mr-[10px] mb-[10px] w-[280px] rounded-lg overflow-hidden border border-color"
|
||||||
|
v-for="(item, index) in attachment.data"
|
||||||
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }"
|
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }"
|
||||||
:key="index" @click="selectedFile = item">
|
:key="index" @click="selectedFile = item">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@ -47,18 +53,14 @@
|
|||||||
<div v-if="item.value.news_item.length > 1">
|
<div v-if="item.value.news_item.length > 1">
|
||||||
<template v-for="(newsItem, newsIndex) in item.value.news_item">
|
<template v-for="(newsItem, newsIndex) in item.value.news_item">
|
||||||
<div class="px-[15px] py-[10px] flex" :class="{'border-b border-color' : newsIndex < item.value.news_item.length - 1 }" v-if="newsIndex > 0">
|
<div class="px-[15px] py-[10px] flex" :class="{'border-b border-color' : newsIndex < item.value.news_item.length - 1 }" v-if="newsIndex > 0">
|
||||||
<div class="flex-1 w-0 truncate">
|
<div class="flex-1 w-0 truncate">{{ newsItem.title }}</div>
|
||||||
{{ newsItem.title }}
|
|
||||||
</div>
|
|
||||||
<div class="w-[50px] h-[50px] ml-[10px]">
|
<div class="w-[50px] h-[50px] ml-[10px]">
|
||||||
<el-image :src="newsItem.thumb_url" class="w-full h-full" />
|
<el-image :src="newsItem.thumb_url" class="w-full h-full" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-[15px] py-[10px]" v-else>
|
<div class="px-[15px] py-[10px]" v-else>{{ item.value.news_item[0].title }}</div>
|
||||||
{{ item.value.news_item[0].title }}
|
|
||||||
</div>
|
|
||||||
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
||||||
<icon name="element Select" color="#fff" size="40px" />
|
<icon name="element Select" color="#fff" size="40px" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -83,8 +83,7 @@
|
|||||||
<el-form-item :label="t('businessDomain')">
|
<el-form-item :label="t('businessDomain')">
|
||||||
<el-input :model-value="wechatStatic.business_domain" placeholder="Please input" class="input-width" :readonly="true">
|
<el-input :model-value="wechatStatic.business_domain" placeholder="Please input" class="input-width" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
|
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -92,8 +91,7 @@
|
|||||||
<el-form-item :label="t('jsSecureDomain')">
|
<el-form-item :label="t('jsSecureDomain')">
|
||||||
<el-input :model-value="wechatStatic.js_secure_domain" placeholder="Please input" class="input-width" :readonly="true">
|
<el-input :model-value="wechatStatic.js_secure_domain" placeholder="Please input" class="input-width" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
|
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -101,8 +99,7 @@
|
|||||||
<el-form-item :label="t('webAuthDomain')">
|
<el-form-item :label="t('webAuthDomain')">
|
||||||
<el-input :model-value="wechatStatic.web_auth_domain" placeholder="Please input" class="input-width" :readonly="true">
|
<el-input :model-value="wechatStatic.web_auth_domain" placeholder="Please input" class="input-width" :readonly="true">
|
||||||
<template #append>
|
<template #append>
|
||||||
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
|
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|||||||
@ -12,9 +12,7 @@
|
|||||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
|
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="flex items-center text-[14px]">{{ t('writingTipsOne1') }}--<el-button link type="primary"
|
<p class="flex items-center text-[14px]">{{ t('writingTipsOne1') }}--<el-button link type="primary" @click="linkEvent">{{ t('writingTipsOne2') }}</el-button>, {{ t('writingTipsOne3') }}<span class="text-primary">URL / Token / EncondingAESKey</span>{{ t('writingTipsOne4') }}</p>
|
||||||
@click="linkEvent">{{ t('writingTipsOne2') }}</el-button>, {{ t('writingTipsOne3') }}<span
|
|
||||||
class="text-primary">URL / Token / EncondingAESKey</span>{{ t('writingTipsOne4') }}</p>
|
|
||||||
<div class="w-[100%] mt-[10px]">
|
<div class="w-[100%] mt-[10px]">
|
||||||
<img class="w-[100%]" src="@/app/assets/images/setting/wechat_1.png" />
|
<img class="w-[100%]" src="@/app/assets/images/setting/wechat_1.png" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -83,7 +83,7 @@
|
|||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getKeywordsReplyInfo, editKeywordsReply, addKeywordsReply } from '@/app/api/wechat'
|
import { getKeywordsReplyInfo, editKeywordsReply, addKeywordsReply } from '@/app/api/wechat'
|
||||||
import { ElMessage, FormInstance, FormRules } from 'element-plus'
|
import { FormInstance, FormRules } from 'element-plus'
|
||||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import ReplyForm from '@/app/views/channel/wechat/components/reply-form.vue'
|
import ReplyForm from '@/app/views/channel/wechat/components/reply-form.vue'
|
||||||
|
|||||||
@ -110,60 +110,6 @@ const setFormData = async (row: any = null) => {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证手机号格式
|
|
||||||
const mobileVerify = (rule: any, value: any, callback: any) => {
|
|
||||||
if (value && !/^1[3-9]\d{9}$/.test(value)) {
|
|
||||||
callback(new Error(t('generateMobile')))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证身份证号
|
|
||||||
const idCardVerify = (rule: any, value: any, callback: any) => {
|
|
||||||
if (value && !/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value)) {
|
|
||||||
callback(new Error(t('generateIdCard')))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证邮箱号
|
|
||||||
const emailVerify = (rule: any, value: any, callback: any) => {
|
|
||||||
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
|
|
||||||
callback(new Error(t('generateEmail')))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证至少输入1个字符
|
|
||||||
const minInputVerify = (rule: any, value: any, callback: any) => {
|
|
||||||
if (value && !/^\d{0,}$/.test(value)) {
|
|
||||||
callback(new Error(t('generateMin')))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证最多输入150个字符
|
|
||||||
const maxInputVerify = (rule: any, value: any, callback: any) => {
|
|
||||||
if (value && !/^\d{0,150}$/.test(value)) {
|
|
||||||
callback(new Error(t('generateMax')))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证请输入整数
|
|
||||||
const numberVerify = (rule: any, value: any, callback: any) => {
|
|
||||||
if (!Number.isInteger(value)) {
|
|
||||||
callback(new Error(t('generateNumber')))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
showDialog,
|
showDialog,
|
||||||
setFormData
|
setFormData
|
||||||
|
|||||||
@ -27,10 +27,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import { FormRules } from 'element-plus'
|
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import useDiyStore from '@/stores/modules/diy'
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
const diyStore = useDiyStore()
|
const diyStore = useDiyStore()
|
||||||
@ -70,12 +69,23 @@ const open = (option:any) => {
|
|||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const formRules = reactive<FormRules>({
|
const formRules = computed(() => {
|
||||||
|
return {
|
||||||
title: [
|
title: [
|
||||||
{ required: true, message: "请输入颜色名称", trigger: 'blur' }
|
{ required: true, message: "请输入颜色名称", trigger: 'blur' }
|
||||||
],
|
],
|
||||||
value: [
|
value: [
|
||||||
{ required: true, message: "请输入颜色value值", trigger: 'blur' }
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (rule: any, value: any, callback: any) => {
|
||||||
|
if (!value) {
|
||||||
|
callback('请输入颜色value值')
|
||||||
|
} else{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}
|
||||||
],
|
],
|
||||||
label: [
|
label: [
|
||||||
{ required: true, message: "请输入颜色key值", trigger: 'blur' },
|
{ required: true, message: "请输入颜色key值", trigger: 'blur' },
|
||||||
@ -93,14 +103,16 @@ const formRules = reactive<FormRules>({
|
|||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const confirmFn = async (formEl: FormInstance | undefined) => {
|
const confirmFn = async (formEl: FormInstance | undefined) => {
|
||||||
if (confirmRepeat.value || !formEl) return
|
if (confirmRepeat.value) return
|
||||||
await formEl.validate(async (valid) => {
|
await formRef.value?.validate(async (valid) => {
|
||||||
if (confirmRepeat.value) return
|
if (confirmRepeat.value) return
|
||||||
confirmRepeat.value = true
|
confirmRepeat.value = true
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
confirmRepeat.value = false
|
||||||
emit('confirm', cloneDeep(formData));
|
emit('confirm', cloneDeep(formData));
|
||||||
dialogThemeVisible.value = false;
|
dialogThemeVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,9 @@
|
|||||||
|
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
<template v-for="(item,index) in titleStyleList" :key="index">
|
<template v-for="(item,index) in titleStyleList" :key="index">
|
||||||
<div :class="{ 'border-primary': selectTitleStyle.value == item.value }" @click="changeTitleStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
|
<div :class="{ 'border-primary': selectTitleStyle.value == item.value }"
|
||||||
|
@click="changeTitleStyle(item)"
|
||||||
|
class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
|
||||||
<img :src="img(item.url)" />
|
<img :src="img(item.url)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -53,7 +55,8 @@
|
|||||||
<el-form label-width="90px" class="px-[10px]">
|
<el-form label-width="90px" class="px-[10px]">
|
||||||
|
|
||||||
<el-form-item :label="t('selectStyle')" class="flex">
|
<el-form-item :label="t('selectStyle')" class="flex">
|
||||||
<span class="text-primary flex-1 cursor-pointer" @click="showBlockStyle">{{ diyStore.editComponent.blockStyle.title }}</span>
|
<span class="text-primary flex-1 cursor-pointer"
|
||||||
|
@click="showBlockStyle">{{ diyStore.editComponent.blockStyle.title }}</span>
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<ArrowRight />
|
<ArrowRight />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -62,7 +65,9 @@
|
|||||||
<el-dialog v-model="showListDialog" :title="t('selectStyle')" width="600px">
|
<el-dialog v-model="showListDialog" :title="t('selectStyle')" width="600px">
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
<template v-for="(item,index) in blockStyleList" :key="index">
|
<template v-for="(item,index) in blockStyleList" :key="index">
|
||||||
<div :class="{ 'border-primary': selectBlockStyle.value == item.value }" @click="changeBlockStyle(item)" class="flex items-center justify-center overflow-hidden w-[250px] h-[150px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
|
<div :class="{ 'border-primary': selectBlockStyle.value == item.value }"
|
||||||
|
@click="changeBlockStyle(item)"
|
||||||
|
class="flex items-center justify-center overflow-hidden w-[250px] h-[150px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
|
||||||
<img :src="img(item.url)" />
|
<img :src="img(item.url)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -81,13 +86,15 @@
|
|||||||
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
|
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
|
||||||
|
|
||||||
<div ref="blockBoxRef">
|
<div ref="blockBoxRef">
|
||||||
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
|
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id"
|
||||||
|
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
|
||||||
<el-form-item :label="t('image')">
|
<el-form-item :label="t('image')">
|
||||||
<upload-image v-model="item.imageUrl" :limit="1" />
|
<upload-image v-model="item.imageUrl" :limit="1" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('activeCubeTitle')">
|
<el-form-item :label="t('activeCubeTitle')">
|
||||||
<el-input v-model.trim="item.title.text" :placeholder="t('activeCubeTitlePlaceholder')" clearable maxlength="4" show-word-limit/>
|
<el-input v-model.trim="item.title.text" :placeholder="t('activeCubeTitlePlaceholder')"
|
||||||
|
clearable maxlength="4" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('activeCubeSubTitleTextColor')" v-show="diyStore.editComponent.blockStyle.value == 'style-3'">
|
<el-form-item :label="t('activeCubeSubTitleTextColor')" v-show="diyStore.editComponent.blockStyle.value == 'style-3'">
|
||||||
@ -115,7 +122,8 @@
|
|||||||
<el-color-picker v-model="item.listFrame.endColor" show-alpha :predefine="diyStore.predefineColors" />
|
<el-color-picker v-model="item.listFrame.endColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<div v-show="diyStore.editComponent.blockStyle.value != 'style-4' && diyStore.editComponent.blockStyle.value != 'style-3'">
|
<div
|
||||||
|
v-show="diyStore.editComponent.blockStyle.value != 'style-4' && diyStore.editComponent.blockStyle.value != 'style-3'">
|
||||||
<el-form-item :label="t('activeCubeButton')">
|
<el-form-item :label="t('activeCubeButton')">
|
||||||
<el-input v-model.trim="item.moreTitle.text" :placeholder="t('activeCubeButtonPlaceholder')" clearable maxlength="3" show-word-limit />
|
<el-input v-model.trim="item.moreTitle.text" :placeholder="t('activeCubeButtonPlaceholder')" clearable maxlength="3" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -131,13 +139,17 @@
|
|||||||
<diy-link v-model="item.link" />
|
<diy-link v-model="item.link" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.list.length > 1" @click="diyStore.editComponent.list.splice(index,1)">
|
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
|
||||||
|
v-show="diyStore.editComponent.list.length > 1"
|
||||||
|
@click="diyStore.editComponent.list.splice(index,1)">
|
||||||
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
|
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addItem">{{ t('activeCubeAddItem') }}</el-button>
|
<el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addItem">
|
||||||
|
{{ t('activeCubeAddItem') }}
|
||||||
|
</el-button>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -160,7 +172,8 @@
|
|||||||
<h3 class="mb-[10px]">{{ t('subTitleStyle') }}</h3>
|
<h3 class="mb-[10px]">{{ t('subTitleStyle') }}</h3>
|
||||||
<el-form label-width="90px" class="px-[10px]">
|
<el-form label-width="90px" class="px-[10px]">
|
||||||
<el-form-item :label="t('textColor')">
|
<el-form-item :label="t('textColor')">
|
||||||
<el-color-picker v-model="diyStore.editComponent.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors"/>
|
<el-color-picker v-model="diyStore.editComponent.subTitle.textColor" show-alpha
|
||||||
|
:predefine="diyStore.predefineColors" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('subTextBgColor')">
|
<el-form-item :label="t('subTextBgColor')">
|
||||||
<el-color-picker v-model="diyStore.editComponent.subTitle.startColor" show-alpha :predefine="diyStore.predefineColors" />
|
<el-color-picker v-model="diyStore.editComponent.subTitle.startColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||||
|
|||||||
@ -82,7 +82,8 @@
|
|||||||
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
|
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
|
||||||
|
|
||||||
<div ref="searchHotWordTabBoxRef">
|
<div ref="searchHotWordTabBoxRef">
|
||||||
<div v-for="(item,index) in diyStore.editComponent.search.hotWord.list" :key="item.id" class="item-wrap p-[10px] relative border border-dashed border-gray-300 mb-[16px]">
|
<div v-for="(item,index) in diyStore.editComponent.search.hotWord.list" :key="item.id"
|
||||||
|
class="item-wrap p-[10px] relative border border-dashed border-gray-300 mb-[16px]">
|
||||||
|
|
||||||
<el-form-item :label="t('carouselSearchHotWordText')" class="!mb-0">
|
<el-form-item :label="t('carouselSearchHotWordText')" class="!mb-0">
|
||||||
<el-input v-model.trim="item.text" :placeholder="t('carouselSearchHotWordTextPlaceholder')" clearable maxlength="4" show-word-limit />
|
<el-input v-model.trim="item.text" :placeholder="t('carouselSearchHotWordTextPlaceholder')" clearable maxlength="4" show-word-limit />
|
||||||
@ -131,7 +132,9 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.tab.list.length > 1" @click="diyStore.editComponent.tab.list.splice(index,1)">
|
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
|
||||||
|
v-show="diyStore.editComponent.tab.list.length > 1"
|
||||||
|
@click="diyStore.editComponent.tab.list.splice(index,1)">
|
||||||
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
|
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -141,7 +144,9 @@
|
|||||||
|
|
||||||
<!-- 选择微页面弹出框 -->
|
<!-- 选择微页面弹出框 -->
|
||||||
<el-dialog v-model="diyPageShowDialog" :title="t('selectSourcesDiyPage')" width="1000px" :close-on-press-escape="true" :destroy-on-close="true" :close-on-click-modal="false">
|
<el-dialog v-model="diyPageShowDialog" :title="t('selectSourcesDiyPage')" width="1000px" :close-on-press-escape="true" :destroy-on-close="true" :close-on-click-modal="false">
|
||||||
<el-table :data="diyPageTable.data" ref="diyPageTableRef" size="large" v-loading="diyPageTable.loading" height="490px" @current-change="handleCurrentDiyPageChange" row-key="id" highlight-current-row>
|
<el-table :data="diyPageTable.data" ref="diyPageTableRef" size="large"
|
||||||
|
v-loading="diyPageTable.loading" height="490px"
|
||||||
|
@current-change="handleCurrentDiyPageChange" row-key="id" highlight-current-row>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<span>{{ !diyPageTable.loading ? t('emptyData') : '' }}</span>
|
<span>{{ !diyPageTable.loading ? t('emptyData') : '' }}</span>
|
||||||
</template>
|
</template>
|
||||||
@ -150,8 +155,10 @@
|
|||||||
<el-table-column prop="type_name" :label="t('diyPageForAddon')" min-width="80" />
|
<el-table-column prop="type_name" :label="t('diyPageForAddon')" min-width="80" />
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="mt-[16px] flex justify-end">
|
<div class="mt-[16px] flex justify-end">
|
||||||
<el-pagination v-model:current-page="diyPageTable.page" v-model:page-size="diyPageTable.limit"
|
<el-pagination v-model:current-page="diyPageTable.page"
|
||||||
layout="total, sizes, prev, pager, next, jumper" :total="diyPageTable.total"
|
v-model:page-size="diyPageTable.limit"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="diyPageTable.total"
|
||||||
@size-change="loadDiyPageList" @current-change="loadDiyPageList" />
|
@size-change="loadDiyPageList" @current-change="loadDiyPageList" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-end mt-[15px]">
|
<div class="flex items-center justify-end mt-[15px]">
|
||||||
@ -175,12 +182,15 @@
|
|||||||
<div class="text-sm text-gray-400 mb-[10px]">{{ t('carouselSearchSwiperTips') }}</div>
|
<div class="text-sm text-gray-400 mb-[10px]">{{ t('carouselSearchSwiperTips') }}</div>
|
||||||
|
|
||||||
<div ref="imageBoxRef">
|
<div ref="imageBoxRef">
|
||||||
<div v-for="(item,index) in diyStore.editComponent.swiper.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
|
<div v-for="(item,index) in diyStore.editComponent.swiper.list" :key="item.id"
|
||||||
|
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
|
||||||
<el-form-item :label="t('image')">
|
<el-form-item :label="t('image')">
|
||||||
<upload-image v-model="item.imageUrl" :limit="1" @change="selectImg" />
|
<upload-image v-model="item.imageUrl" :limit="1" @change="selectImg" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.swiper.list.length > 1" @click="diyStore.editComponent.swiper.list.splice(index,1)">
|
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
|
||||||
|
v-show="diyStore.editComponent.swiper.list.length > 1"
|
||||||
|
@click="diyStore.editComponent.swiper.list.splice(index,1)">
|
||||||
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
|
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -190,7 +200,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-button v-show="diyStore.editComponent.swiper.list.length < 10" class="w-full" @click="addImageAd">{{ t('addImageAd') }}</el-button>
|
<el-button v-show="diyStore.editComponent.swiper.list.length < 10" class="w-full"
|
||||||
|
@click="addImageAd">{{ t('addImageAd') }}
|
||||||
|
</el-button>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
@ -270,10 +282,12 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('topRounded')">
|
<el-form-item :label="t('topRounded')">
|
||||||
<el-slider v-model="diyStore.editComponent.swiper.topRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
|
<el-slider v-model="diyStore.editComponent.swiper.topRounded" show-input size="small"
|
||||||
|
class="ml-[10px] diy-nav-slider" :max="50" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('bottomRounded')">
|
<el-form-item :label="t('bottomRounded')">
|
||||||
<el-slider v-model="diyStore.editComponent.swiper.bottomRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
|
<el-slider v-model="diyStore.editComponent.swiper.bottomRounded" show-input size="small"
|
||||||
|
class="ml-[10px] diy-nav-slider" :max="50" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -415,7 +429,8 @@ diyStore.editComponent.swiper.list.forEach((item: any) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const activeNames = ref(['tab', 'swiper'])
|
const activeNames = ref(['tab', 'swiper'])
|
||||||
const handleChange = (val: string[]) => {}
|
const handleChange = (val: string[]) => {
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadDiyPageList()
|
loadDiyPageList()
|
||||||
@ -507,8 +522,7 @@ const diyPageTable = reactive({
|
|||||||
total: 0,
|
total: 0,
|
||||||
loading: true,
|
loading: true,
|
||||||
data: [],
|
data: [],
|
||||||
searchParam: {
|
searchParam: {}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const diyPageTableRef = ref<InstanceType<typeof ElTable>>()
|
const diyPageTableRef = ref<InstanceType<typeof ElTable>>()
|
||||||
|
|
||||||
@ -633,6 +647,7 @@ defineExpose({})
|
|||||||
.select-diy-page-input .el-input__inner {
|
.select-diy-page-input .el-input__inner {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapse-wrap {
|
.collapse-wrap {
|
||||||
.el-collapse-item__header {
|
.el-collapse-item__header {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<el-dialog v-model="dialogThemeVisible" title="编辑色调" width="850px" align-center destroy-on-close="true">
|
<el-dialog v-model="dialogThemeVisible" title="编辑色调" width="850px" align-center destroy-on-close="true">
|
||||||
<el-form :model="openData" label-width="150px" :rules="formRules">
|
<el-form :model="openData" label-width="150px" :rules="formRules">
|
||||||
<el-form-item label="色调名称" prop="title">
|
<el-form-item label="色调名称" prop="title">
|
||||||
<el-input v-model="openData.title" placeholder="请输入色调名称" maxlength="15" class="!w-[250px]" :disabled="openData.id != ''" />
|
<el-input v-model="openData.title" placeholder="请输入色调名称" maxlength="15" class="!w-[250px]" :disabled="openData.id != ''" @keydown.enter.native.prevent />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
|||||||
@ -238,8 +238,8 @@ route.query.title = route.query.title || ''
|
|||||||
route.query.back = route.query.back || '/admin/diy/list'
|
route.query.back = route.query.back || '/admin/diy/list'
|
||||||
|
|
||||||
const backPath = route.query.back
|
const backPath = route.query.back
|
||||||
const template = ref('');
|
const template = ref('')
|
||||||
const oldTemplate = ref('');
|
const oldTemplate = ref('')
|
||||||
const wapUrl = ref('')
|
const wapUrl = ref('')
|
||||||
const wapDomain = ref('')
|
const wapDomain = ref('')
|
||||||
const wapPreview = ref('')
|
const wapPreview = ref('')
|
||||||
@ -273,7 +273,7 @@ const originData = reactive({
|
|||||||
const isChange = ref(true) // 数据是否发生变化,true:没变化,false:变化了
|
const isChange = ref(true) // 数据是否发生变化,true:没变化,false:变化了
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
if (isChange.value) {
|
if (isChange.value) {
|
||||||
location.href = `${location.origin}${backPath}`;
|
location.href = `${location.origin}${backPath}`
|
||||||
router.push(backPath)
|
router.push(backPath)
|
||||||
} else {
|
} else {
|
||||||
// 数据发生变化,弹框提示:确定离开此页面
|
// 数据发生变化,弹框提示:确定离开此页面
|
||||||
@ -287,7 +287,7 @@ const goBack = () => {
|
|||||||
autofocus: false
|
autofocus: false
|
||||||
}
|
}
|
||||||
).then(() => {
|
).then(() => {
|
||||||
location.href = `${location.origin}${backPath}`;
|
location.href = `${location.origin}${backPath}`
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -337,7 +337,7 @@ const changeTemplatePage = (value:any)=> {
|
|||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
diyStore.changeCurrentIndex(-99)
|
diyStore.changeCurrentIndex(-99)
|
||||||
diyStore.init(); // 清空
|
diyStore.init() // 清空
|
||||||
if (value) {
|
if (value) {
|
||||||
let data = cloneDeep(templatePages[value].data);
|
let data = cloneDeep(templatePages[value].data);
|
||||||
diyStore.global = data.global;
|
diyStore.global = data.global;
|
||||||
@ -349,13 +349,13 @@ const changeTemplatePage = (value:any)=> {
|
|||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// 还原
|
// 还原
|
||||||
template.value = oldTemplate.value;
|
template.value = oldTemplate.value
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
diyStore.init(); // 清空
|
diyStore.init() // 清空
|
||||||
if (value) {
|
if (value) {
|
||||||
let data = cloneDeep(templatePages[value].data);
|
let data = cloneDeep(templatePages[value].data)
|
||||||
diyStore.global = data.global;
|
diyStore.global = data.global
|
||||||
if (data.value.length) {
|
if (data.value.length) {
|
||||||
diyStore.value = data.value
|
diyStore.value = data.value
|
||||||
}
|
}
|
||||||
@ -444,7 +444,7 @@ initPage({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDiyTemplatePages(data.type);
|
loadDiyTemplatePages(data.type)
|
||||||
|
|
||||||
// 加载预览
|
// 加载预览
|
||||||
wapDomain.value = data.domain_url.wap_domain
|
wapDomain.value = data.domain_url.wap_domain
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-switch v-model="diyStore.editComponent.field.privacyProtection" />
|
<el-switch v-model="diyStore.editComponent.field.privacyProtection" :disabled ="diyStore.editComponent.addressFormat != 'province/city/district/address'" />
|
||||||
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏地址,仅管理员可查看') }}</div>
|
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏地址,仅管理员可查看') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { ref } from 'vue'
|
import { ref,watch } from 'vue'
|
||||||
import useDiyStore from '@/stores/modules/diy'
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
const diyStore = useDiyStore()
|
const diyStore = useDiyStore()
|
||||||
@ -66,6 +66,15 @@ diyStore.editComponent.verify = (index: number) => {
|
|||||||
const res = { code: true, message: '' }
|
const res = { code: true, message: '' }
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
watch(
|
||||||
|
() => diyStore.editComponent.addressFormat,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal !== 'province/city/district/address') {
|
||||||
|
diyStore.editComponent.field.privacyProtection = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
defineExpose({})
|
defineExpose({})
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,147 @@
|
|||||||
|
|
||||||
<!-- 表单组件 字段内容设置 -->
|
<!-- 表单组件 字段内容设置 -->
|
||||||
<slot name="field"></slot>
|
<slot name="field"></slot>
|
||||||
todo 此处编写表格组件的属性
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('列设置')">
|
||||||
|
<div ref="imageBoxRef">
|
||||||
|
<div v-for="(item, index) in diyStore.editComponent.columnList" :key="item.id"
|
||||||
|
class="border-b-[1px] border-[#e0e0e0] py-1">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex">
|
||||||
|
<span :class="['iconfont', 'ml-[5px]', 'cursor-pointer', getIconClass(item.type)]"></span>
|
||||||
|
<el-input v-model="item.name" class="input-style" :input-style="{ boxShadow: 'none' }"
|
||||||
|
:placeholder="t('请输入列名')" />
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<span v-if="diyStore.editComponent.columnList.length > 1" @click="removeOption(index)"
|
||||||
|
class="cursor-pointer ml-[5px] nc-iconfont nc-icon-shanchu-yuangaizhiV6xx"></span>
|
||||||
|
<span class="cursor-pointer ml-[5px] nc-iconfont nc-icon-xiaV6xx"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.type == 'radio'" class="flex">
|
||||||
|
<div class="text-[#999] mr-3" >{{ item.options?.length || 0 }}个选项</div>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]" @click="openRadioDialog(item, index)">{{ t('编辑') }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.type == 'date'" class="flex">
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]" @click="openRadioDialog(item, index)">{{ t('设置日期格式') }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.type == 'address'" class="flex">
|
||||||
|
<div class="text-[#999] mr-3">精确到详细地址</div>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]" @click="openRadioDialog(item, index)">{{ t('设置') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-popover placement="bottom" :width="50" trigger="hover">
|
||||||
|
<template #reference>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]">{{ t('添加') }}</span>
|
||||||
|
</template>
|
||||||
|
<div v-for="(item, index) in columnTypeOptions" :key="index" @click="addOption(item)"
|
||||||
|
class="cursor-pointer hover:bg-[#d1e1ff] rounded text-center">
|
||||||
|
<div class="py-1 text-[var(--el-text-color-primary]">{{ item.label }}</div>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('是否自增')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.autoIncrementControl" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('填写限制')" v-if="diyStore.editComponent.autoIncrementControl">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>默认显示</span>
|
||||||
|
<el-input v-model="diyStore.editComponent.writeLimit.default" class="input-short" :placeholder="t('')" />
|
||||||
|
<span>项</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center my-1">
|
||||||
|
<span>最少填写</span>
|
||||||
|
<el-input v-model="diyStore.editComponent.writeLimit.min" class="input-short" :placeholder="t('')" />
|
||||||
|
<span>项</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>最多填写</span>
|
||||||
|
<el-input v-model="diyStore.editComponent.writeLimit.max" class="input-short" :placeholder="t('')" />
|
||||||
|
<span>项</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('按钮名称')" v-if="diyStore.editComponent.autoIncrementControl">
|
||||||
|
<el-input v-model="diyStore.editComponent.btnText" :placeholder="t('请输入按钮名称')" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<!-- 单选项 -->
|
||||||
|
<!-- <el-dialog v-model="radioDialogVisible" :title="t('设置单选项')" width="500">
|
||||||
|
<div v-if="activeColumnTemp.type == 'radio'">
|
||||||
|
<el-form label-width="80px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('选项名称')">
|
||||||
|
<el-input v-model="activeColumnTemp.name" :input-style="{ boxShadow: 'none' }" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('设置选项')">
|
||||||
|
<div ref="radioBoxRef">
|
||||||
|
<div v-for="(opt, idx) in activeColumnTemp.options" :key="opt.id">
|
||||||
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<div class="flex-1">
|
||||||
|
<el-input v-model="opt.label" :input-style="{ boxShadow: 'none' }"
|
||||||
|
:placeholder="t('请输入')" />
|
||||||
|
</div>
|
||||||
|
<span v-if="activeColumnTemp.options.length > 1" @click="removeOptionItem(idx)"
|
||||||
|
class="cursor-pointer ml-[5px] nc-iconfont nc-icon-shanchu-yuangaizhiV6xx"></span>
|
||||||
|
<span class="cursor-pointer ml-[5px] nc-iconfont nc-icon-iconpaixu1"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]" @click="addOptionItem">{{ t('添加选项') }}</span>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]" @click="addOtherOption">{{ t('添加其它项') }}</span>
|
||||||
|
<el-popover :visible="visible" placement="bottom" :width="300">
|
||||||
|
<p class="mb-[5px]">{{ t('addMultipleOption') }}</p>
|
||||||
|
<p class="text-[#888] text-[12px] mb-[5px]">{{ t('addOptionTips') }}</p>
|
||||||
|
<el-input v-model.trim="optionsValue" type="textarea" clearable maxlength="200"
|
||||||
|
show-word-limit />
|
||||||
|
<div class="mt-[10px] text-right">
|
||||||
|
<el-button size="small" text @click="visible = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button size="small" type="primary" @click="batchAddOptions">{{t('confirm')}}</el-button>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<span class="text-primary cursor-pointer"
|
||||||
|
@click="visible = true">{{ t('addMultipleOption') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="activeColumnTemp.type == 'date'">
|
||||||
|
<el-form>
|
||||||
|
<el-form-item :label="t('dataFormat')">
|
||||||
|
<el-radio-group v-model="activeColumnTemp.dateFormat" class="!block">
|
||||||
|
<el-radio class="!block" label="YYYY年M月D日">{{ dateFormat.format1 }}</el-radio>
|
||||||
|
<el-radio class="!block" label="YYYY-MM-DD">{{ dateFormat.format2 }}</el-radio>
|
||||||
|
<el-radio class="!block" label="YYYY/MM/DD">{{ dateFormat.format3 }}</el-radio>
|
||||||
|
<el-radio class="!block" label="YYYY-MM-DD HH:mm">{{ dateFormat.format4 }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="activeColumnTemp.type == 'address'">
|
||||||
|
<el-form>
|
||||||
|
<el-form-item :label="t('地址格式')">
|
||||||
|
<el-radio-group v-model="activeColumnTemp.addressFormat" class="!block">
|
||||||
|
<el-radio class="!block" label="province/city/district/address">{{ t('省/市/区/街道/详细地址') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province/city/district/street">{{ t('省/市/区/街道(镇)') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province/city/district">{{ t('省/市/区(县)') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province/city">{{ t('省/市') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province">{{ t('省') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="radioDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleDialogConfirm">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog> -->
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 表单组件 其他设置 -->
|
<!-- 表单组件 其他设置 -->
|
||||||
<slot name="other"></slot>
|
<slot name="other"></slot>
|
||||||
@ -25,20 +165,248 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { ref } from 'vue'
|
import Sortable from 'sortablejs'
|
||||||
|
import { ref, watch, onMounted, nextTick ,reactive, computed} from 'vue'
|
||||||
import useDiyStore from '@/stores/modules/diy'
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import { range } from 'lodash-es'
|
||||||
const diyStore = useDiyStore()
|
const diyStore = useDiyStore()
|
||||||
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
// 组件验证
|
// 组件验证
|
||||||
diyStore.editComponent.verify = (index: number) => {
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
const res = { code: true, message: '' }
|
const res = { code: true, message: '' }
|
||||||
// todo 只需要考虑该组件自身的验证
|
// todo 只需要考虑该组件自身的验证
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 类型选项数组
|
||||||
|
const columnTypeOptions = ref([
|
||||||
|
{ label: '单选项', value: 'radio' },
|
||||||
|
{ label: '文本', value: 'text' },
|
||||||
|
{ label: '数字', value: 'number' },
|
||||||
|
{ label: '手机号', value: 'mobile' },
|
||||||
|
{ label: '地址', value: 'address' },
|
||||||
|
{ label: '身份证', value: 'idcard' },
|
||||||
|
{ label: '性别', value: 'gender' },
|
||||||
|
{ label: '日期', value: 'date' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const getIconClass = (type:any) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'radio':
|
||||||
|
return 'icona-duihaopc30'
|
||||||
|
case 'text':
|
||||||
|
return 'icona-danhangwenben-1pc30'
|
||||||
|
case 'number':
|
||||||
|
return 'icona-shuzipc30-1'
|
||||||
|
case 'mobile':
|
||||||
|
return 'icona-shoujipc30'
|
||||||
|
case 'address':
|
||||||
|
return 'iconbiaotipc'
|
||||||
|
case 'idcard':
|
||||||
|
return 'icona-shenfenzhengpc30'
|
||||||
|
case 'gender':
|
||||||
|
return 'el-icon-s-opportunity'
|
||||||
|
case 'date':
|
||||||
|
return 'icona-riqipc30'
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageBoxRef = ref()
|
||||||
|
const generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2, 5)
|
||||||
|
// 添加列方法
|
||||||
|
const addOption = (item) => {
|
||||||
|
const newColumn: any = {
|
||||||
|
id: generateId(),
|
||||||
|
name: item.label,
|
||||||
|
type: item.value, // 列类型
|
||||||
|
value: '' // 默认值(可选)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是单选项,初始化 options
|
||||||
|
if (item.value === 'radio') {
|
||||||
|
newColumn.options = [
|
||||||
|
{ id: generateId(), label: '选项1' },
|
||||||
|
{ id: generateId(), label: '选项2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
// 如果是日期,初始化 dateFormat
|
||||||
|
if (item.value === 'date') {
|
||||||
|
newColumn.dateFormat = 'YYYY年M月D日' // 默认日期格式
|
||||||
|
}
|
||||||
|
// 如果是地址,初始化 addressFormat
|
||||||
|
if (item.value === 'address') {
|
||||||
|
newColumn.addressFormat = 'province/city/district/address' // 默认日期格式
|
||||||
|
}
|
||||||
|
|
||||||
|
diyStore.editComponent.columnList.push(newColumn)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeOption = (index: number) => {
|
||||||
|
diyStore.editComponent.columnList.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// nextTick(() => {
|
||||||
|
// if (diyStore.editComponent.columnList.length < 2) return;
|
||||||
|
// const sortable = Sortable.create(imageBoxRef.value, {
|
||||||
|
// group: 'item-wrap',
|
||||||
|
// animation: 200,
|
||||||
|
// onEnd: event => {
|
||||||
|
// const temp = diyStore.editComponent.columnList[event.oldIndex!]
|
||||||
|
// diyStore.editComponent.columnList.splice(event.oldIndex!, 1)
|
||||||
|
// diyStore.editComponent.columnList.splice(event.newIndex!, 0, temp)
|
||||||
|
// sortable.sort(
|
||||||
|
// range(diyStore.editComponent.columnList.length).map(value => {
|
||||||
|
// return value.toString()
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
console.log(diyStore.editComponent.columnList);
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeColumn = ref<any>({}) // 真正数据(原始数据,不动它)
|
||||||
|
const activeColumnTemp = ref<any>({}) // 弹窗编辑临时副本
|
||||||
|
const activeRadioIndex = ref(0) // 当前编辑列的下标
|
||||||
|
|
||||||
|
const radioDialogVisible = ref(false)
|
||||||
|
const radioBoxRef = ref()
|
||||||
|
|
||||||
|
const optionsValue = ref('')
|
||||||
|
const visible = ref(false)
|
||||||
|
const dateFormat: any = reactive({
|
||||||
|
format1: '',
|
||||||
|
format2: '',
|
||||||
|
format3: '',
|
||||||
|
format4: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const openRadioDialog = (item, index) => {
|
||||||
|
activeRadioIndex.value = index // 记录当前列的下标,方便确定时更新
|
||||||
|
activeColumn.value = item
|
||||||
|
activeColumnTemp.value = JSON.parse(JSON.stringify(item)) // 深拷贝,避免联动
|
||||||
|
if(item.type == 'radio'){
|
||||||
|
if (!activeColumnTemp.value.options) activeColumnTemp.value.options = []
|
||||||
|
radioDialogVisible.value = true
|
||||||
|
// nextTick(() => initRadioSortable()) // 拖拽初始化
|
||||||
|
}else if(item.type == 'date'){
|
||||||
|
// 初始赋值当天日期
|
||||||
|
const today = new Date();
|
||||||
|
let year = today.getFullYear();
|
||||||
|
let month = String(today.getMonth() + 1).padStart(2, '0');
|
||||||
|
let day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
dateFormat.format1 = `${ year }年${ month }月${ day }日`;
|
||||||
|
dateFormat.format2 = `${ year }-${ month }-${ day }`;
|
||||||
|
dateFormat.format3 = `${ year }/${ month }/${ day }`;
|
||||||
|
dateFormat.format4 = `${ year }-${ month }-${ day } ${ hours }:${ minutes }`;
|
||||||
|
radioDialogVisible.value = true
|
||||||
|
} else if(item.type == 'address'){
|
||||||
|
radioDialogVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化拖拽
|
||||||
|
// const initRadioSortable = () => {
|
||||||
|
// Sortable.create(radioBoxRef.value, {
|
||||||
|
// group: 'radio-option-wrap',
|
||||||
|
// animation: 200,
|
||||||
|
// draggable: '.drag-radio-item',
|
||||||
|
// onEnd: event => {
|
||||||
|
// const options = activeColumnTemp.value.options // 注意!这里用 temp 的
|
||||||
|
// const temp = options[event.oldIndex!]
|
||||||
|
// options.splice(event.oldIndex!, 1)
|
||||||
|
// options.splice(event.newIndex!, 0, temp)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
const handleDialogConfirm = () => {
|
||||||
|
console.log(activeColumnTemp.value);
|
||||||
|
|
||||||
|
diyStore.editComponent.columnList[activeRadioIndex.value] = JSON.parse(JSON.stringify(activeColumnTemp.value)) // 同步副本到原数据
|
||||||
|
radioDialogVisible.value = false // 关闭弹窗
|
||||||
|
}
|
||||||
|
|
||||||
|
const addOptionItem = () => {
|
||||||
|
const newOption = { id: generateId(), label: '选项' + (activeColumnTemp.value.options.length + 1) }
|
||||||
|
activeColumnTemp.value.options.push(newOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addOtherOption = () => {
|
||||||
|
const newOption = { id: generateId(), label: '其他' }
|
||||||
|
activeColumnTemp.value.options.push(newOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeOptionItem = (index: number) => {
|
||||||
|
activeColumnTemp.value.options.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数组去重
|
||||||
|
const uniqueByKey = (arr: any, key: any) => {
|
||||||
|
const seen = new Set();
|
||||||
|
return arr.filter((item: any) => {
|
||||||
|
const serializedKey = JSON.stringify(item[key]);
|
||||||
|
return seen.has(serializedKey) ? false : seen.add(serializedKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 批量添加
|
||||||
|
const batchAddOptions = () => {
|
||||||
|
if (optionsValue.value.trim()) {
|
||||||
|
const newOptions = optionsValue.value.split(',').map((option: any) => {
|
||||||
|
return {
|
||||||
|
id: diyStore.generateRandom(),
|
||||||
|
label: option.trim()
|
||||||
|
};
|
||||||
|
}).filter((option: any) => option.label !== '');
|
||||||
|
|
||||||
|
// 去除重复的选项
|
||||||
|
const uniqueNewOptions = uniqueByKey(newOptions, 'label');
|
||||||
|
|
||||||
|
// 过滤掉已存在的选项
|
||||||
|
const filteredNewOptions = uniqueNewOptions.filter(newOption =>
|
||||||
|
!activeColumnTemp.value.options.some(existingOption => existingOption.label === newOption.label)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果有新的选项,添加到选项列表中
|
||||||
|
if (filteredNewOptions.length > 0) {
|
||||||
|
activeColumnTemp.value.options.push(...filteredNewOptions);
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: t('errorTipsTwo'),
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsValue.value = '';
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
defineExpose({})
|
defineExpose({})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
:deep(.input-style .el-input__wrapper) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-short{
|
||||||
|
width: 80px;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|||||||
@ -5,13 +5,25 @@
|
|||||||
<!-- 表单组件 字段内容设置 -->
|
<!-- 表单组件 字段内容设置 -->
|
||||||
<slot name="field"></slot>
|
<slot name="field"></slot>
|
||||||
<el-form label-width="100px" class="px-[10px]">
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
<el-form-item :label="t('上传方式')">
|
<el-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('上传方式') }}</span>
|
||||||
|
<el-tooltip effect="light" :content="t('拍摄时长限制1分钟,从相册上传不限制时长。')" placement="top">
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<el-radio-group v-model="diyStore.editComponent.uploadMode">
|
<el-radio-group v-model="diyStore.editComponent.uploadMode">
|
||||||
<el-radio label="shoot_and_album">{{ t('拍摄和相册') }}</el-radio>
|
<el-radio label="shoot_and_album">{{ t('拍摄和相册') }}</el-radio>
|
||||||
<el-radio label="shoot_only">{{ t('只允许拍摄') }}</el-radio>
|
<el-radio label="shoot_only">{{ t('只允许拍摄') }}</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -50,7 +50,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="update_time" :label="t('updateTime')" min-width="120" />
|
<el-table-column prop="update_time" :label="t('updateTime')" min-width="120" />
|
||||||
|
|
||||||
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="100">
|
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="130">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end">
|
||||||
<el-button type="primary" v-if="row.status == 1 && row.type=='DIY_FORM'" link @click="spreadEvent(row)">{{ t('promotion') }}</el-button>
|
<el-button type="primary" v-if="row.status == 1 && row.type=='DIY_FORM'" link @click="spreadEvent(row)">{{ t('promotion') }}</el-button>
|
||||||
@ -156,7 +156,7 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 推广弹出框 -->
|
<!-- 推广弹出框 -->
|
||||||
<form-spread-popup ref="formSpreadPopupRef" />
|
<spread-popup ref="spreadPopupRef" />
|
||||||
|
|
||||||
<!-- 表单提交成功页弹出框 -->
|
<!-- 表单提交成功页弹出框 -->
|
||||||
<form-submit-popup ref="formSubmitPopupRef" @complete="loadDiyFormList" />
|
<form-submit-popup ref="formSubmitPopupRef" @complete="loadDiyFormList" />
|
||||||
@ -181,9 +181,9 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
|
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import recordsDetail from '@/app/views/diy_form/records.vue'
|
import recordsDetail from '@/app/views/diy_form/records.vue'
|
||||||
import formSpreadPopup from '@/app/views/diy_form/components/form-spread-popup.vue'
|
|
||||||
import formSubmitPopup from '@/app/views/diy_form/components/form-submit-popup.vue'
|
import formSubmitPopup from '@/app/views/diy_form/components/form-submit-popup.vue'
|
||||||
import formWritePopup from '@/app/views/diy_form/components/form-write-popup.vue'
|
import formWritePopup from '@/app/views/diy_form/components/form-write-popup.vue'
|
||||||
|
import spreadPopup from '@/components/spread-popup/index.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -506,12 +506,19 @@ const shareEvent = async (formEl: FormInstance | undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 表单推广
|
// 表单推广
|
||||||
const formSpreadPopupRef: any = ref(null)
|
const spreadPopupRef = ref(null)
|
||||||
|
|
||||||
const spreadEvent = (data: any) => {
|
const spreadEvent = (data: any) => {
|
||||||
formSpreadPopupRef.value.show(data)
|
const pagePath = "/app/pages/index/diy_form"
|
||||||
|
const columnName = "form_id"
|
||||||
|
const columnValue = data.form_id
|
||||||
|
const title = "表单推广"
|
||||||
|
const folder = "diy_form"
|
||||||
|
|
||||||
|
spreadPopupRef.value?.show(pagePath, columnName, columnValue, title,folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 表单提交成功页弹出框
|
// 表单提交成功页弹出框
|
||||||
const formSubmitPopupRef: any = ref(null)
|
const formSubmitPopupRef: any = ref(null)
|
||||||
|
|
||||||
|
|||||||
@ -106,7 +106,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- <el-table-column fixed prop="create_time" :label="t('填表时间')" min-width="120" /> -->
|
<!-- <el-table-column fixed prop="create_time" :label="t('填表时间')" min-width="120" /> -->
|
||||||
<el-table-column fixed prop="create_time" :label="t('fillInFormTotal')" min-width="500">
|
<el-table-column fixed prop="create_time" :label="t('fillInFormTotal')" min-width="500">
|
||||||
<template #default="{ row }" @click="">
|
<template #default="{ row }">
|
||||||
{{ row.write_count }}
|
{{ row.write_count }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@ -155,9 +155,11 @@
|
|||||||
<div class="flex mb-[10px]" v-for="(item, index) in formDetail" :key="index">
|
<div class="flex mb-[10px]" v-for="(item, index) in formDetail" :key="index">
|
||||||
<div class="flex justify-end w-[100px]">{{ item.label }}:</div>
|
<div class="flex justify-end w-[100px]">{{ item.label }}:</div>
|
||||||
<div class="flex ml-[20px]">
|
<div class="flex ml-[20px]">
|
||||||
<div v-if="Array.isArray(item.text)" class="mr-[10px]" v-for="(textItem, i) in item.text" :key="i">
|
<template v-if="Array.isArray(item.text)">
|
||||||
|
<div class="mr-[10px]" v-for="(textItem, i) in item.text" :key="i">
|
||||||
{{ textItem }}
|
{{ textItem }}
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
<div v-else>{{ item.text }}</div>
|
<div v-else>{{ item.text }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -180,11 +182,10 @@
|
|||||||
import { reactive, ref, defineAsyncComponent } from 'vue'
|
import { reactive, ref, defineAsyncComponent } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getDiyFormFieldsList, getDiyFormFieldStat, getFormRecords,getFormRecordsInfo,deleteFormRecords,getFormRecordsMember} from '@/app/api/diy_form'
|
import { getDiyFormFieldsList, getDiyFormFieldStat, getFormRecords,getFormRecordsInfo,deleteFormRecords,getFormRecordsMember} from '@/app/api/diy_form'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const showDialog = ref(false)
|
const showDialog = ref(false)
|
||||||
const activeName = ref('detail_data')
|
const activeName = ref('detail_data')
|
||||||
@ -257,7 +258,6 @@ const deleteEvent = (row: any) => {
|
|||||||
form_id: row.form_id
|
form_id: row.form_id
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
initData();
|
initData();
|
||||||
}).catch(() => {
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -303,7 +303,7 @@ const formMemberList = reactive({
|
|||||||
data: [],
|
data: [],
|
||||||
searchParam: {
|
searchParam: {
|
||||||
keyword: '',
|
keyword: '',
|
||||||
form_id: 0,
|
form_id: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ const getFormRecordsMemberFn = (page: number = 1) => {
|
|||||||
formMemberList.loading = false;
|
formMemberList.loading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
formMemberList.loading = false;
|
formMemberList.loading = false;
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//查看会员详情
|
//查看会员详情
|
||||||
@ -387,5 +387,4 @@ defineExpose({
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -126,7 +126,7 @@
|
|||||||
<span class="w-[70px] flex-shrink-0 text-right">{{t('account') }}:</span>
|
<span class="w-[70px] flex-shrink-0 text-right">{{t('account') }}:</span>
|
||||||
<span>{{ row.transfer_account }}</span>
|
<span>{{ row.transfer_account }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center" v-if="row.transfer_payment_code">
|
<div class="flex items-start" v-if="row.transfer_payment_code">
|
||||||
<span class="w-[70px] flex-shrink-0 text-right">{{ t('transferCode') }}:</span>
|
<span class="w-[70px] flex-shrink-0 text-right">{{ t('transferCode') }}:</span>
|
||||||
<el-image :src="img(row.transfer_payment_code)" :preview-src-list="[img(row.transfer_payment_code)]" :hide-on-click-modal="true" class="w-[50px] h-[50px]"></el-image>
|
<el-image :src="img(row.transfer_payment_code)" :preview-src-list="[img(row.transfer_payment_code)]" :hide-on-click-modal="true" class="w-[50px] h-[50px]"></el-image>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -52,13 +52,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, computed } from 'vue'
|
import { reactive, ref, computed,defineEmits } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getPayRefundInfo, getRefundType, getRefundTransfer } from '@/app/api/pay'
|
import { getPayRefundInfo, getRefundType, getRefundTransfer } from '@/app/api/pay'
|
||||||
import { FormInstance, ElMessage } from 'element-plus'
|
import { FormInstance } from 'element-plus'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { img, filterNumber } from '@/utils/common'
|
|
||||||
import useAppStore from '@/stores/modules/app'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -71,7 +69,6 @@ let refundNo = '';
|
|||||||
const refundList = ref([])
|
const refundList = ref([])
|
||||||
const formData: Record<string, any> = ref(null)
|
const formData: Record<string, any> = ref(null)
|
||||||
|
|
||||||
|
|
||||||
const handleClose = (done: () => void) => {
|
const handleClose = (done: () => void) => {
|
||||||
showDialog.value = false;
|
showDialog.value = false;
|
||||||
}
|
}
|
||||||
@ -109,6 +106,7 @@ const transferEvent = (data:any) => {
|
|||||||
transferDialog.value = true
|
transferDialog.value = true
|
||||||
transferFormData.refund_no = data.refund_no
|
transferFormData.refund_no = data.refund_no
|
||||||
transferFormData.refund_money = data.money
|
transferFormData.refund_money = data.money
|
||||||
|
transferFormData.voucher = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialFormData = {
|
const initialFormData = {
|
||||||
@ -127,6 +125,7 @@ const formRules = computed(() => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const emit = defineEmits(['loadPayRefundList'])
|
||||||
|
|
||||||
const confirm = async (formEl: FormInstance | undefined) => {
|
const confirm = async (formEl: FormInstance | undefined) => {
|
||||||
if (loading.value || !formEl) return
|
if (loading.value || !formEl) return
|
||||||
@ -140,6 +139,7 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
|||||||
transferDialog.value = false
|
transferDialog.value = false
|
||||||
refundList.value = []
|
refundList.value = []
|
||||||
getRefundListInfo(refundNo)
|
getRefundListInfo(refundNo)
|
||||||
|
emit('loadPayRefundList')
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
transferDialog.value = false
|
transferDialog.value = false
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|||||||
@ -72,8 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-image-viewer :url-list="previewImageList" v-if="imageViewerShow" @close="imageViewerShow = false" :initial-index="0"
|
<el-image-viewer :url-list="previewImageList" v-if="imageViewerShow" @close="imageViewerShow = false" :initial-index="0" :zoom-rate="1" />
|
||||||
:zoom-rate="1" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@ -56,14 +56,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { getPayDetail, payAuditPass, payAuditRefuse } from '@/app/api/sys'
|
import { getPayDetail, payAuditPass, payAuditRefuse } from '@/app/api/sys'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
|
||||||
const pageName = route.meta.title
|
const pageName = route.meta.title
|
||||||
const id: number = parseInt((route.query.id || 0))
|
const id: number = parseInt((route.query.id || 0))
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
|||||||
@ -10,7 +10,14 @@
|
|||||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||||
<el-form :inline="true" :model="payRefundTable.searchParam" ref="searchFormRef">
|
<el-form :inline="true" :model="payRefundTable.searchParam" ref="searchFormRef">
|
||||||
<el-form-item :label="t('refundNo')" prop="refund_no">
|
<el-form-item :label="t('refundNo')" prop="refund_no">
|
||||||
<el-input v-model.trim="payRefundTable.searchParam.refund_no" :placeholder="t('refundNoPlaceholder')" />
|
<el-input v-model.trim="payRefundTable.searchParam.refund_no"
|
||||||
|
:placeholder="t('refundNoPlaceholder')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('status')" prop="status">
|
||||||
|
<el-select v-model="payRefundTable.searchParam.status" clearable class="input-width">
|
||||||
|
<el-option :label="t('selectPlaceholder')" value="" />
|
||||||
|
<el-option :label="item" :value="key" v-for="(item, key) in refundStatusList" :key="key" />
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('createTime')" prop="create_time">
|
<el-form-item :label="t('createTime')" prop="create_time">
|
||||||
<el-date-picker v-model="payRefundTable.searchParam.create_time" type="datetimerange"
|
<el-date-picker v-model="payRefundTable.searchParam.create_time" type="datetimerange"
|
||||||
@ -49,22 +56,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
<refund-detail ref="refundDetailDialog"></refund-detail>
|
<refund-detail @loadPayRefundList="handleMessage" ref="refundDetailDialog"></refund-detail>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getPayRefundPages } from '@/app/api/pay'
|
import { getPayRefundPages ,getRefundStatus} from '@/app/api/pay'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import refundDetail from '@/app/views/finance/components/refund-detail.vue'
|
import refundDetail from '@/app/views/finance/components/refund-detail.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
|
||||||
const pageName = route.meta.title
|
const pageName = route.meta.title
|
||||||
|
const refundStatusList = ref([])
|
||||||
|
const checkStatusList = async () => {
|
||||||
|
refundStatusList.value = await (await getRefundStatus()).data
|
||||||
|
}
|
||||||
|
checkStatusList()
|
||||||
const payRefundTable = reactive({
|
const payRefundTable = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@ -73,6 +83,7 @@ const payRefundTable = reactive({
|
|||||||
data: [],
|
data: [],
|
||||||
searchParam: {
|
searchParam: {
|
||||||
refund_no: '',
|
refund_no: '',
|
||||||
|
status: '',
|
||||||
create_time: []
|
create_time: []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -99,6 +110,9 @@ const loadPayRefundList = (page: number = 1) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
loadPayRefundList()
|
loadPayRefundList()
|
||||||
|
const handleMessage = () => {
|
||||||
|
loadPayRefundList()
|
||||||
|
}
|
||||||
const refundDetailDialog: Record<string, any> | null = ref(null)
|
const refundDetailDialog: Record<string, any> | null = ref(null)
|
||||||
const infoEvent = (res:any) => {
|
const infoEvent = (res:any) => {
|
||||||
let data = {no: res.refund_no};
|
let data = {no: res.refund_no};
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
<div class="px-[20px] pb-[10px] font-bold mt-[40px]">{{ t('weapp') }}</div>
|
<div class="px-[20px] pb-[10px] font-bold mt-[40px]">{{ t('weapp') }}</div>
|
||||||
<el-form label-width="40px" class="px-[20px]">
|
<el-form label-width="40px" class="px-[20px]">
|
||||||
<el-form-item label=" " v-if="weappConfig.qr_code">
|
<el-form-item label=" " v-if="weappConfig.qr_code">
|
||||||
<el-image class="w-[100px] h-[100px]" :src="img(weappConfig.qr_code)" />
|
<el-image class="w-[150px] h-[150px]" :src="img(weappConfig.qr_code)" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label=" " v-else>
|
<el-form-item label=" " v-else>
|
||||||
<span class="text-gray-400">{{ t('weappNotSet') }}</span>
|
<span class="text-gray-400">{{ t('weappNotSet') }}</span>
|
||||||
|
|||||||
@ -2,9 +2,8 @@
|
|||||||
<!--应用市场-->
|
<!--应用市场-->
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<el-card class="box-card !border-none" shadow="never">
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<span class="text-page-title">{{ t('localAppText') }}</span>
|
<span class="text-page-title">{{ t("localAppText") }}</span>
|
||||||
|
|
||||||
<el-input class="!w-[250px]" :placeholder="t('search')" v-model.trim="search_name" @keyup.enter="query">
|
<el-input class="!w-[250px]" :placeholder="t('search')" v-model.trim="search_name" @keyup.enter="query">
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
@ -15,48 +14,53 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between my-[20px]">
|
<div class="flex justify-between items-center my-[20px]">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'installed' }]" @click="activeNameTabFn('installed')">
|
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'installed' }]" @click="activeNameTabFn('installed')">{{ t("installLabel") }}</div>
|
||||||
{{ t('installLabel') }}
|
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'uninstalled' }]" @click="activeNameTabFn('uninstalled')">{{ t("uninstalledLabel") }}</div>
|
||||||
</div>
|
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'all' }]" @click="activeNameTabFn('all')">{{ t("buyLabel") }}</div>
|
||||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'uninstalled' }]" @click="activeNameTabFn('uninstalled')">
|
<div :class="['relative flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'recentlyUpdated' }]" @click="activeNameTabFn('recentlyUpdated')">
|
||||||
{{ t('uninstalledLabel') }}
|
<span v-if="localList['recentlyUpdated'].length > 0" class="w-[9px] h-[9px] bg-[#FF0000]" style="position: absolute; border-radius: 50%; right: 5px; top: -5px"></span>
|
||||||
</div>
|
<span>{{ t('recentlyUpdated') }}</span>
|
||||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'all' }]" @click="activeNameTabFn('all')">
|
|
||||||
{{ t('buyLabel') }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" round @click="handleCloudBuild" :loading="cloudBuildRef?.loading">{{ t('cloudBuild') }}</el-button>
|
<div>
|
||||||
|
<el-button type="primary" v-show="activeName === 'recentlyUpdated'" round @click="batchUpgrade" :loading="upgradeRef?.loading" :disabled="authLoading">{{ t("batchUpgrade") }}</el-button>
|
||||||
|
<el-button type="primary" round @click="handleCloudBuild" :loading="cloudBuildRef?.loading" :disabled="authLoading">{{ t("cloudBuild") }}</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<el-table v-if="localList[activeName].length&&!authLoading" :data="info[activeName]" size="large" class="pt-[5px]">
|
<el-table v-if="localList[activeName].length && !loading" :data="info[activeName]" size="large" class="pt-[5px]" @selection-change="handleSelectionChange">
|
||||||
<el-table-column :label="t('appName')" align="left" width="320">
|
<el-table-column type="selection" v-if="activeName === 'recentlyUpdated'" />
|
||||||
|
<el-table-column :label="t('appName')" align="left" width="450">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="flex items-center cursor-pointer" @click = "handleTips">
|
<div class="flex items-center cursor-pointer">
|
||||||
<el-image class="w-[54px] h-[54px]" :src="row.icon" fit="contain">
|
<el-image class="w-[54px] h-[54px]" :src="row.icon" fit="contain">
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="flex items-center w-full h-full">
|
<div class="flex items-center w-full h-full">
|
||||||
<img class="max-w-full max-h-full" src="@/app/assets/images/icon-addon.png" alt="">
|
<img class="max-w-full max-h-full" src="@/app/assets/images/icon-addon.png" alt="" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-image>
|
</el-image>
|
||||||
<div class="flex flex-col justify-center pl-[20px] font-500 text-[13px]">
|
<div class="flex-1 w-0 flex flex-col justify-center pl-[20px] font-500 text-[13px]">
|
||||||
<div class="w-[236px] truncate leading-[18px]">{{ row.title }}</div>
|
<div class="w-[236px] truncate leading-[18px]">{{ row.title }}</div>
|
||||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-if="row.install_info && Object.keys(row.install_info)?.length">{{ row.install_info.version }}</div>
|
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-if="row.install_info && Object.keys(row.install_info)?.length">{{ row.install_info.version }}</div>
|
||||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-else>{{ row.version }}</div>
|
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-else>{{ row.version }}</div>
|
||||||
<div class="mt-[3px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">
|
<div class="mt-[3px] flex flex-nowrap">
|
||||||
<el-tag type="danger" size="small">{{ t('newVersion') }}{{ row.version }}</el-tag>
|
<el-tag type="danger" size="small" v-if="activeName == 'recentlyUpdated' && row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">{{ t("newVersion") }}{{ row.version }}</el-tag>
|
||||||
|
<el-tooltip v-if="versionJudge(row)" effect="dark" content="该插件与框架版本不兼容,可能存在未知问题" placement="top-start">
|
||||||
|
<el-tag type="info" size="small" class="ml-[3px]">该插件与框架版本不兼容,可能存在未知问题</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="left" min-width="120">
|
<el-table-column align="left" min-width="150">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-500 text-[13px] mr-[5px]">{{ t('appIdentification') }}</span>
|
<span class="font-500 text-[13px] mr-[5px]">{{ t("appIdentification") }}</span>
|
||||||
<el-tooltip class="box-item" effect="light" :content="t('tipText')" placement="bottom">
|
<el-tooltip class="box-item" effect="light" :content="t('tipText')" placement="bottom">
|
||||||
<el-icon class="cursor-pointer text-[16px] text-[#a9a9a9]">
|
<el-icon class="cursor-pointer text-[16px] text-[#a9a9a9]">
|
||||||
<QuestionFilled />
|
<QuestionFilled />
|
||||||
@ -73,65 +77,67 @@
|
|||||||
<span class="font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
|
<span class="font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="t('type')" align="left" min-width="100">
|
<el-table-column :label="t('type')" align="left" min-width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="font-500 text-[13px]">{{ row.type === 'app' ? t('app') : t('addon') }}</span>
|
<span class="font-500 text-[13px]">{{ row.type === "app" ? t("app") : t("addon") }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="" :label="t('author')" align="left" min-width="100">
|
<el-table-column prop="" :label="t('author')" align="left" min-width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="font-500 text-[13px]">{{ row.author }}</span>
|
<span class="font-500 text-[13px]">{{ row.author }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="200">
|
<el-table-column :label="t('operation')" fixed="right" align="right" width="250">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version" type="primary" link @click="upgradeAddonFn(row.key)">{{ t('upgrade') }}</el-button>
|
<el-button class="!text-[13px]" v-if="activeName == 'recentlyUpdated' && row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version" type="primary" link @click="upgradeAddonFn(row.key)">{{ t("upgrade") }}</el-button>
|
||||||
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length" type="primary" link @click="uninstallAddonFn(row.key)">{{ t('unload') }}</el-button>
|
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length" type="primary" link @click="uninstallAddonFn(row.key)">{{ t("unload") }}</el-button>
|
||||||
<template v-if="row.is_download && (!row.install_info || !Object.keys(row.install_info).length)">
|
<template v-if="row.is_download && (!row.install_info || !Object.keys(row.install_info).length)">
|
||||||
<el-button class="!text-[13px]" type="primary" link @click="installAddonFn(row.key)">{{ t('install') }}</el-button>
|
<el-button class="!text-[13px]" type="primary" link @click="installAddonFn(row.key)">{{ t("install") }}</el-button>
|
||||||
<el-button class="!text-[13px]" type="primary" link @click="deleteAddonFn(row.key)">{{ t('delete') }}</el-button>
|
<el-button class="!text-[13px]" type="primary" link @click="deleteAddonFn(row.key)">{{ t("delete") }}</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-button class="!text-[13px]" v-if="!row.is_download" :loading="downloading == row.key" :disabled="downloading != ''" type="primary" link @click.stop="downEvent(row)">
|
<el-button class="!text-[13px]" v-if="!row.is_download" :loading="downloading == row.key" :disabled="downloading != ''" type="primary" link @click.stop="downEvent(row)">
|
||||||
<span>{{ t('down') }}</span>
|
<span>{{ t("down") }}</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button class="!text-[13px]" type="primary" link @click="getAddonDetialFn(row)">{{ t('detail') }}</el-button>
|
<el-button class="!text-[13px]" type="primary" link @click="getAddonDetailFn(row)">{{ t("detail") }}</el-button>
|
||||||
|
<el-button class="!text-[13px]" type="primary" link @click="updateInformationFn(row)">更新信息</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="data-loading" v-if="authLoading || !localList[activeName].length">
|
<div class="data-loading" v-if="loading || !localList[activeName].length">
|
||||||
<el-table :data="[]" size="large" class="pt-[5px]">
|
<el-table :data="[]" size="large" class="pt-[5px]">
|
||||||
<el-table-column :label="t('appName')" align="left" width="320"></el-table-column>
|
<el-table-column :label="t('appName')" align="left" width="320" />
|
||||||
<el-table-column align="left" min-width="120"></el-table-column>
|
<el-table-column align="left" min-width="120" />
|
||||||
<el-table-column prop="" :label="t('introduction')" align="left" min-width="200"></el-table-column>
|
<el-table-column :label="t('introduction')" align="left" min-width="200" />
|
||||||
<el-table-column :label="t('type')" align="left" min-width="100"></el-table-column>
|
<el-table-column :label="t('type')" align="left" min-width="100" />
|
||||||
<el-table-column prop="" :label="t('author')" align="left" min-width="100"></el-table-column>
|
<el-table-column :label="t('author')" align="left" min-width="100" />
|
||||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="150"></el-table-column>
|
<el-table-column :label="t('operation')" fixed="right" align="right" width="150" />
|
||||||
<template #empty><span></span></template>
|
<template #empty>
|
||||||
|
<span></span>
|
||||||
|
</template>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="h-[100px]" v-loading="authLoading" v-if="authLoading">
|
<div class="h-[100px]" v-loading="loading" v-if="loading"></div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading && activeName == 'installed' && !authLoading">
|
<el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading && activeName == 'installed' && !authLoading">
|
||||||
<template #image>
|
<template #image>
|
||||||
<div class="w-[230px] mx-auto">
|
<div class="w-[230px] mx-auto">
|
||||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #description>
|
<template #description>
|
||||||
<p class="flex items-center">{{ t('installed-empty') }}</p>
|
<p class="flex items-center">{{ t("installed-empty") }}</p>
|
||||||
</template>
|
</template>
|
||||||
</el-empty>
|
</el-empty>
|
||||||
<el-empty class="mx-auto overview-empty" v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled' && !authLoading">
|
<el-empty class="mx-auto overview-empty" v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled' && !authLoading">
|
||||||
<template #image>
|
<template #image>
|
||||||
<div class="w-[230px] mx-auto">
|
<div class="w-[230px] mx-auto">
|
||||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #description>
|
<template #description>
|
||||||
<p class="flex items-center">
|
<p class="flex items-center">
|
||||||
<span>{{ t('descriptionLeft') }}</span>
|
<span>{{ t("descriptionLeft") }}</span>
|
||||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
|
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t("link") }}</el-link>
|
||||||
<span>{{ t('descriptionRight') }}</span>
|
<span>{{ t("descriptionRight") }}</span>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</el-empty>
|
</el-empty>
|
||||||
@ -141,8 +147,7 @@
|
|||||||
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
|
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
|
||||||
<el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click" class="mt-[8px]">
|
<el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click" class="mt-[8px]">
|
||||||
<div class="px-[18px] py-[8px]">
|
<div class="px-[18px] py-[8px]">
|
||||||
<p class="leading-[32px] text-[14px]">
|
<p class="leading-[32px] text-[14px]">您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
||||||
您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
|
||||||
<div class="flex justify-end mt-[36px]">
|
<div class="flex justify-end mt-[36px]">
|
||||||
<el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
|
<el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
|
||||||
<el-button class="w-[100px] !h-[48px]" plain @click="getAuthCodeDialog.hide()">关闭</el-button>
|
<el-button class="w-[100px] !h-[48px]" plain @click="getAuthCodeDialog.hide()">关闭</el-button>
|
||||||
@ -157,17 +162,27 @@
|
|||||||
<el-empty class="mx-auto overview-empty" v-if="!localList.all.length && !loading && authinfo && activeName == 'all' && !authLoading">
|
<el-empty class="mx-auto overview-empty" v-if="!localList.all.length && !loading && authinfo && activeName == 'all' && !authLoading">
|
||||||
<template #image>
|
<template #image>
|
||||||
<div class="w-[230px] mx-auto">
|
<div class="w-[230px] mx-auto">
|
||||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #description>
|
<template #description>
|
||||||
<p class="flex items-center">
|
<p class="flex items-center">
|
||||||
<span>{{ t('buyDescriptionLeft') }}</span>
|
<span>{{ t("buyDescriptionLeft") }}</span>
|
||||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
|
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t("link") }}</el-link>
|
||||||
<span>{{ t('descriptionRight') }}</span>
|
<span>{{ t("descriptionRight") }}</span>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</el-empty>
|
</el-empty>
|
||||||
|
<el-empty class="mx-auto overview-empty" v-if="!localList.recentlyUpdated.length && !loading && authinfo && activeName == 'recentlyUpdated' && !authLoading">
|
||||||
|
<template #image>
|
||||||
|
<div class="w-[230px] mx-auto">
|
||||||
|
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<p class="flex items-center">{{ t("recentlyUpdatedEmpty") }}</p>
|
||||||
|
</template>
|
||||||
|
</el-empty>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
|
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
|
||||||
@ -183,13 +198,14 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-sm mt-[10px] text-info">{{ t('authInfoTips') }}</div>
|
<div class="text-sm mt-[10px] text-info">{{ t("authInfoTips") }}</div>
|
||||||
|
|
||||||
<div class="mt-[20px]">
|
<div class="mt-[20px]">
|
||||||
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t('confirm') }}</el-button>
|
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t("confirm") }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[10px] text-right">
|
<div class="mt-[10px] text-right">
|
||||||
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
|
<el-button type="primary" link @click="market">{{ t("notHaveAuth") }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -212,7 +228,7 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button type="primary" @click="appStoreShowDialog = false">{{ t('confirm') }}</el-button>
|
<el-button type="primary" @click="appStoreShowDialog = false">{{ t("confirm") }}</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@ -228,17 +244,17 @@
|
|||||||
<el-scrollbar max-height="50vh">
|
<el-scrollbar max-height="50vh">
|
||||||
<div class="min-h-[150px]">
|
<div class="min-h-[150px]">
|
||||||
<div class="my-3" v-if="installCheckResult.dir">
|
<div class="my-3" v-if="installCheckResult.dir">
|
||||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
<p class="pt-[20px] pl-[20px]">{{ t("dirPermission") }}</p>
|
||||||
<div class="px-[20px] pt-[10px] text-[14px]">
|
<div class="px-[20px] pt-[10px] text-[14px]">
|
||||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<span>{{ t('path') }}</span>
|
<span>{{ t("path") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('demand') }}</span>
|
<span>{{ t("demand") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('status') }}</span>
|
<span>{{ t("status") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in installCheckResult.dir.is_readable" :key="index">
|
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in installCheckResult.dir.is_readable" :key="index">
|
||||||
@ -246,10 +262,14 @@
|
|||||||
<span>{{ item.dir }}</span>
|
<span>{{ item.dir }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('readable') }}</span>
|
<span>{{ t("readable") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
<span v-if="item.status">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<el-icon color="red">
|
<el-icon color="red">
|
||||||
<CloseBold />
|
<CloseBold />
|
||||||
@ -262,10 +282,14 @@
|
|||||||
<span>{{ item.dir }}</span>
|
<span>{{ item.dir }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('write') }}</span>
|
<span>{{ t("write") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
<span v-if="item.status">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<el-icon color="red">
|
<el-icon color="red">
|
||||||
<CloseBold />
|
<CloseBold />
|
||||||
@ -279,10 +303,10 @@
|
|||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<el-tooltip effect="dark" :content="t('installTips')" placement="top">
|
<el-tooltip effect="dark" :content="t('installTips')" placement="top">
|
||||||
<el-button type="default" :disabled="!installCheckResult.is_pass || cloudInstalling" :loading="localInstalling" @click="handleInstall">{{ t('localInstall') }}</el-button>
|
<el-button :disabled="!installCheckResult.is_pass || cloudInstalling" :loading="localInstalling" @click="handleInstall">{{ t("localInstall") }}</el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip effect="dark" :content="t('cloudInstallTips')" placement="top">
|
<el-tooltip effect="dark" :content="t('cloudInstallTips')" placement="top">
|
||||||
<el-button type="primary" :disabled="!installCheckResult.is_pass || localInstalling" :loading="cloudInstalling" @click="handleCloudInstall">{{ t('cloudInstall') }}</el-button>
|
<el-button type="primary" :disabled="!installCheckResult.is_pass || localInstalling" :loading="cloudInstalling" @click="handleCloudInstall">{{ t("cloudInstall") }}</el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -302,17 +326,17 @@
|
|||||||
<el-scrollbar max-height="50vh">
|
<el-scrollbar max-height="50vh">
|
||||||
<div class="min-h-[150px]">
|
<div class="min-h-[150px]">
|
||||||
<div class="bg-[#fff] my-3" v-if="uninstallCheckResult.dir">
|
<div class="bg-[#fff] my-3" v-if="uninstallCheckResult.dir">
|
||||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
<p class="pt-[20px] pl-[20px]">{{ t("dirPermission") }}</p>
|
||||||
<div class="px-[20px] pt-[10px] text-[14px]">
|
<div class="px-[20px] pt-[10px] text-[14px]">
|
||||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<span>{{ t('path') }}</span>
|
<span>{{ t("path") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('demand') }}</span>
|
<span>{{ t("demand") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('status') }}</span>
|
<span>{{ t("status") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in uninstallCheckResult.dir.is_readable" :key="index">
|
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in uninstallCheckResult.dir.is_readable" :key="index">
|
||||||
@ -320,10 +344,14 @@
|
|||||||
<span>{{ item.dir }}</span>
|
<span>{{ item.dir }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('readable') }}</span>
|
<span>{{ t("readable") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
<span v-if="item.status">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<el-icon color="red">
|
<el-icon color="red">
|
||||||
<CloseBold />
|
<CloseBold />
|
||||||
@ -336,10 +364,14 @@
|
|||||||
<span>{{ item.dir }}</span>
|
<span>{{ item.dir }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span>{{ t('write') }}</span>
|
<span>{{ t("write") }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
<span v-if="item.status">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<el-icon color="red">
|
<el-icon color="red">
|
||||||
<CloseBold />
|
<CloseBold />
|
||||||
@ -363,20 +395,31 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 更新信息 -->
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
<upgrade-log :upgradeKey="upgradeKey" ref="upgradeLogRef" />
|
||||||
<upgrade ref="upgradeRef" @complete="localListFn"/>
|
<upgrade ref="upgradeRef" @complete="localListFn" @cloudbuild="handleCloudBuild" />
|
||||||
<cloud-build ref="cloudBuildRef" />
|
<cloud-build ref="cloudBuildRef" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, watch, h } from 'vue'
|
import { ref, reactive, watch, h } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, cloudInstallAddon, getAddonInstalltask, getAddonCloudInstallLog, preUninstallCheck, cancelInstall } from '@/app/api/addon'
|
import {
|
||||||
|
getAddonLocal,
|
||||||
|
uninstallAddon,
|
||||||
|
installAddon,
|
||||||
|
preInstallCheck,
|
||||||
|
cloudInstallAddon,
|
||||||
|
getAddonInstalltask,
|
||||||
|
getAddonCloudInstallLog,
|
||||||
|
preUninstallCheck,
|
||||||
|
cancelInstall
|
||||||
|
} from '@/app/api/addon'
|
||||||
import { deleteAddonDevelop } from '@/app/api/tools'
|
import { deleteAddonDevelop } from '@/app/api/tools'
|
||||||
import { getInstallConfig } from '@/app/api/sys'
|
|
||||||
import { downloadVersion, getAuthInfo, setAuthInfo } from '@/app/api/module'
|
import { downloadVersion, getAuthInfo, setAuthInfo } from '@/app/api/module'
|
||||||
|
import { getVersions } from '@/app/api/auth'
|
||||||
import { ElMessage, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus'
|
||||||
import 'vue-web-terminal/lib/theme/dark.css'
|
import 'vue-web-terminal/lib/theme/dark.css'
|
||||||
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
||||||
@ -386,6 +429,7 @@ import { useRouter, useRoute } from 'vue-router'
|
|||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import Upgrade from '@/app/components/upgrade/index.vue'
|
import Upgrade from '@/app/components/upgrade/index.vue'
|
||||||
import CloudBuild from '@/app/components/cloud-build/index.vue'
|
import CloudBuild from '@/app/components/cloud-build/index.vue'
|
||||||
|
import UpgradeLog from '@/app/components/upgrade-log/index.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -398,11 +442,11 @@ const installAfterTips = ref<string[]>([])
|
|||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const unloadHintDialog = ref(false)
|
const unloadHintDialog = ref(false)
|
||||||
const terminalRef = ref(null)
|
const terminalRef = ref(null)
|
||||||
const installPhpConfig = ref(null)
|
const frameworkVersion = ref('')
|
||||||
|
const upgradeLogRef = ref<any>(null)
|
||||||
getInstallConfig().then(({ data }) => {
|
getVersions().then((res) => {
|
||||||
installPhpConfig.value = data
|
frameworkVersion.value = res.data.version.version
|
||||||
}).catch()
|
})
|
||||||
|
|
||||||
const currDownData = ref()
|
const currDownData = ref()
|
||||||
const downEventHintFn = () => {
|
const downEventHintFn = () => {
|
||||||
@ -437,11 +481,10 @@ const downEvent = (param: Record<string, any>, isDown = false) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const authCode = ref('')
|
const authCode = ref('')
|
||||||
getAuthInfo().then(res => {
|
getAuthInfo().then((res) => {
|
||||||
if (res.data.data && res.data.data.auth_code) {
|
if (res.data.data && res.data.data.auth_code) {
|
||||||
authCode.value = res.data.data.auth_code
|
authCode.value = res.data.data.auth_code
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -452,39 +495,47 @@ const search_name = ref('')
|
|||||||
const info = ref({
|
const info = ref({
|
||||||
installed: [],
|
installed: [],
|
||||||
uninstalled: [],
|
uninstalled: [],
|
||||||
all: []
|
all: [],
|
||||||
|
recentlyUpdated: []
|
||||||
})
|
})
|
||||||
const query = () => {
|
const query = () => {
|
||||||
if (search_name.value == '' || search_name.value == null) {
|
if (search_name.value == '' || search_name.value == null) {
|
||||||
info.value.installed = localList.value.installed
|
info.value.installed = localList.value.installed
|
||||||
info.value.uninstalled = localList.value.uninstalled
|
info.value.uninstalled = localList.value.uninstalled
|
||||||
info.value.all = localList.value.all
|
info.value.all = localList.value.all
|
||||||
|
info.value.recentlyUpdated = localList.value.recentlyUpdated
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
info.value.installed = localList.value.installed.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
info.value.installed = localList.value.installed.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||||
info.value.uninstalled = localList.value.uninstalled.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
info.value.uninstalled = localList.value.uninstalled.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||||
info.value.all = localList.value.all.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
info.value.all = localList.value.all.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||||
|
info.value.recentlyUpdated = localList.value.recentlyUpdated.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||||
}
|
}
|
||||||
const localList = ref({
|
const localList = ref({
|
||||||
installed: [],
|
installed: [],
|
||||||
uninstalled: [],
|
uninstalled: [],
|
||||||
all: [],
|
all: [],
|
||||||
|
recentlyUpdated: [],
|
||||||
error: ''
|
error: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const localListFn = () => {
|
const localListFn = () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
getAddonLocal({}).then(res => {
|
getAddonLocal({}).then((res) => {
|
||||||
const data = res.data.list
|
const data = res.data.list
|
||||||
localList.value.error = res.data.error
|
localList.value.error = res.data.error
|
||||||
localList.value.installed = []
|
localList.value.installed = []
|
||||||
localList.value.uninstalled = []
|
localList.value.uninstalled = []
|
||||||
localList.value.all = []
|
localList.value.all = []
|
||||||
|
localList.value.recentlyUpdated = []
|
||||||
for (const i in data) {
|
for (const i in data) {
|
||||||
if (data[i].is_local == false) localList.value.all.push(data[i])
|
if (data[i].is_local == false) localList.value.all.push(data[i])
|
||||||
|
|
||||||
if (data[i].install_info && Object.keys(data[i].install_info)?.length) {
|
if (data[i].install_info && Object.keys(data[i].install_info)?.length) {
|
||||||
localList.value.installed.push(data[i])
|
localList.value.installed.push(data[i])
|
||||||
|
if (data[i].install_info.version != data[i].version) {
|
||||||
|
localList.value.recentlyUpdated.push(data[i])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (data[i].is_download == true) localList.value.uninstalled.push(data[i])
|
if (data[i].is_download == true) localList.value.uninstalled.push(data[i])
|
||||||
}
|
}
|
||||||
@ -506,11 +557,6 @@ const localListFn = () => {
|
|||||||
|
|
||||||
localListFn()
|
localListFn()
|
||||||
|
|
||||||
// 点击应用提示到站点
|
|
||||||
const handleTips = () => {
|
|
||||||
ElMessage('请在站点中运行程序!')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击应用可以进系统
|
// 点击应用可以进系统
|
||||||
|
|
||||||
const appLink: any = ref({})
|
const appLink: any = ref({})
|
||||||
@ -553,7 +599,7 @@ function makeIterator(array: string[]) {
|
|||||||
let nextIndex = 0
|
let nextIndex = 0
|
||||||
return {
|
return {
|
||||||
next () {
|
next () {
|
||||||
if ((nextIndex + 1) == array.length) {
|
if (nextIndex + 1 == array.length) {
|
||||||
nextIndex = 0
|
nextIndex = 0
|
||||||
}
|
}
|
||||||
return { value: array[nextIndex++] }
|
return { value: array[nextIndex++] }
|
||||||
@ -568,13 +614,13 @@ function makeIterator(array: string[]) {
|
|||||||
const installAddonFn = (key: string) => {
|
const installAddonFn = (key: string) => {
|
||||||
currAddon.value = key
|
currAddon.value = key
|
||||||
|
|
||||||
preInstallCheck(key).then(res => {
|
preInstallCheck(key).then((res) => {
|
||||||
installStep.value = 1
|
installStep.value = 1
|
||||||
installShowDialog.value = true
|
installShowDialog.value = true
|
||||||
installAfterTips.value = []
|
installAfterTips.value = []
|
||||||
installCheckResult.value = res.data
|
installCheckResult.value = res.data
|
||||||
userStore.clearRouters()
|
userStore.clearRouters()
|
||||||
}).catch(() => { })
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -582,7 +628,7 @@ const installAddonFn = (key: string) => {
|
|||||||
*/
|
*/
|
||||||
let notificationEl = null
|
let notificationEl = null
|
||||||
const getInstallTask = (first: boolean = true) => {
|
const getInstallTask = (first: boolean = true) => {
|
||||||
getAddonInstalltask().then(res => {
|
getAddonInstalltask().then((res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
if (first) {
|
if (first) {
|
||||||
installLog = []
|
installLog = []
|
||||||
@ -591,10 +637,10 @@ const getInstallTask = (first: boolean = true) => {
|
|||||||
notificationEl = ElNotification.success({
|
notificationEl = ElNotification.success({
|
||||||
title: t('warning'),
|
title: t('warning'),
|
||||||
dangerouslyUseHTMLString: true,
|
dangerouslyUseHTMLString: true,
|
||||||
message: h('div', {}, [
|
message: h('div', {}, [t('installingTips'), h('span', {
|
||||||
t('installingTips'),
|
class: 'text-primary cursor-pointer',
|
||||||
h('span', { class: 'text-primary cursor-pointer', onClick: checkInstallTask }, [t('installPercent')])
|
onClick: checkInstallTask
|
||||||
]),
|
}, [t('installPercent')])]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
showClose: false
|
showClose: false
|
||||||
})
|
})
|
||||||
@ -638,7 +684,7 @@ const handleInstall = () => {
|
|||||||
if (!installCheckResult.value.is_pass || localInstalling.value) return
|
if (!installCheckResult.value.is_pass || localInstalling.value) return
|
||||||
localInstalling.value = true
|
localInstalling.value = true
|
||||||
|
|
||||||
installAddon({ addon: currAddon.value }).then(res => {
|
installAddon({ addon: currAddon.value }).then((res) => {
|
||||||
installStep.value = 3
|
installStep.value = 3
|
||||||
localListFn()
|
localListFn()
|
||||||
localInstalling.value = false
|
localInstalling.value = false
|
||||||
@ -662,27 +708,25 @@ const handleCloudInstall = () => {
|
|||||||
if (!installCheckResult.value.is_pass || cloudInstalling.value) return
|
if (!installCheckResult.value.is_pass || cloudInstalling.value) return
|
||||||
cloudInstalling.value = true
|
cloudInstalling.value = true
|
||||||
|
|
||||||
cloudInstallAddon({ addon: currAddon.value }).then(res => {
|
cloudInstallAddon({ addon: currAddon.value })
|
||||||
|
.then((res) => {
|
||||||
installStep.value = 2
|
installStep.value = 2
|
||||||
terminalRef.value.execute('clear')
|
terminalRef.value.execute('clear')
|
||||||
terminalRef.value.execute('开始安装插件')
|
terminalRef.value.execute('开始安装插件')
|
||||||
getInstallTask()
|
getInstallTask()
|
||||||
cloudInstalling.value = false
|
cloudInstalling.value = false
|
||||||
}).catch((res) => {
|
})
|
||||||
|
.catch((res) => {
|
||||||
cloudInstalling.value = false
|
cloudInstalling.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const authElMessageBox = () => {
|
const authElMessageBox = () => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(t('authTips'), t('warning'), {
|
||||||
t('authTips'),
|
|
||||||
t('warning'),
|
|
||||||
{
|
|
||||||
distinguishCancelAndClose: true,
|
distinguishCancelAndClose: true,
|
||||||
confirmButtonText: t('toBind'),
|
confirmButtonText: t('toBind'),
|
||||||
cancelButtonText: t('toNiucloud')
|
cancelButtonText: t('toNiucloud')
|
||||||
}
|
}).then(() => {
|
||||||
).then(() => {
|
|
||||||
authCodeApproveFn()
|
authCodeApproveFn()
|
||||||
}).catch((action: string) => {
|
}).catch((action: string) => {
|
||||||
if (action === 'cancel') {
|
if (action === 'cancel') {
|
||||||
@ -693,13 +737,12 @@ const authElMessageBox = () => {
|
|||||||
|
|
||||||
let installLog: string[] = []
|
let installLog: string[] = []
|
||||||
const getCloudInstallLog = () => {
|
const getCloudInstallLog = () => {
|
||||||
getAddonCloudInstallLog(currAddon.value)
|
getAddonCloudInstallLog(currAddon.value).then((res) => {
|
||||||
.then(res => {
|
|
||||||
const data = res.data.data ?? []
|
const data = res.data.data ?? []
|
||||||
if (data[0] && data[0].length && installShowDialog.value == true) {
|
if (data[0] && data[0].length && installShowDialog.value == true) {
|
||||||
data[0].forEach(item => {
|
data[0].forEach((item) => {
|
||||||
if (!installLog.includes(item.action)) {
|
if (!installLog.includes(item.action)) {
|
||||||
terminalRef.value.pushMessage({ content: `正在执行:${item.action}` })
|
terminalRef.value.pushMessage({ content: `${ item.action }` })
|
||||||
installLog.push(item.action)
|
installLog.push(item.action)
|
||||||
|
|
||||||
if (item.code == 0) {
|
if (item.code == 0) {
|
||||||
@ -708,8 +751,7 @@ const getCloudInstallLog = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
}).catch(() => {
|
||||||
.catch(() => {
|
|
||||||
notificationEl?.close()
|
notificationEl?.close()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -729,17 +771,13 @@ const uninstallCheckResult = ref({})
|
|||||||
* @param key
|
* @param key
|
||||||
*/
|
*/
|
||||||
const uninstallAddonFn = (key: string) => {
|
const uninstallAddonFn = (key: string) => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(t('uninstallTips'), t('warning'), {
|
||||||
t('uninstallTips'),
|
|
||||||
t('warning'),
|
|
||||||
{
|
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}
|
}).then(() => {
|
||||||
).then(() => {
|
|
||||||
handleUninstallAddon(key)
|
handleUninstallAddon(key)
|
||||||
}).catch(() => { })
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -762,13 +800,11 @@ const handleCloudBuild = () => {
|
|||||||
cloudBuildRef.value?.open()
|
cloudBuildRef.value?.open()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'),
|
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'), {
|
||||||
{
|
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}
|
}).then(() => {
|
||||||
).then(() => {
|
|
||||||
cloudBuildRef.value?.open()
|
cloudBuildRef.value?.open()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -776,7 +812,7 @@ const handleCloudBuild = () => {
|
|||||||
const handleUninstallAddon = (key: string) => {
|
const handleUninstallAddon = (key: string) => {
|
||||||
preUninstallCheck(key).then(({ data }) => {
|
preUninstallCheck(key).then(({ data }) => {
|
||||||
if (data.is_pass) {
|
if (data.is_pass) {
|
||||||
uninstallAddon({ addon: key }).then(res => {
|
uninstallAddon({ addon: key }).then((res) => {
|
||||||
localListFn()
|
localListFn()
|
||||||
userStore.clearRouters()
|
userStore.clearRouters()
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@ -791,7 +827,7 @@ const handleUninstallAddon = (key: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const market = () => {
|
const market = () => {
|
||||||
window.open(installPhpConfig.value.website_url)
|
window.open('https://www.niucloud.com/app')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -800,34 +836,40 @@ const market = () => {
|
|||||||
*/
|
*/
|
||||||
const installShowDialogClose = (done: () => {}) => {
|
const installShowDialogClose = (done: () => {}) => {
|
||||||
if (installStep.value == 2) {
|
if (installStep.value == 2) {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(t('installShowDialogCloseTips'), t('warning'), {
|
||||||
t('installShowDialogCloseTips'),
|
|
||||||
t('warning'),
|
|
||||||
{
|
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}
|
}).then(() => {
|
||||||
).then(() => {
|
|
||||||
cancelInstall(currAddon.value)
|
cancelInstall(currAddon.value)
|
||||||
done()
|
done()
|
||||||
}).catch(() => { })
|
})
|
||||||
} else if (installStep.value == 3) {
|
} else if (installStep.value == 3) {
|
||||||
activeNameTabFn('installed')
|
activeNameTabFn('installed')
|
||||||
location.reload()
|
location.reload()
|
||||||
} else done()
|
} else {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
|
||||||
flashInterval && clearInterval(flashInterval)
|
flashInterval && clearInterval(flashInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插件详情
|
// 插件详情
|
||||||
const appStoreShowDialog = ref(false)
|
const appStoreShowDialog = ref(false)
|
||||||
const appStoreInfo = ref<AnyObject>({})
|
const appStoreInfo = ref({})
|
||||||
const getAddonDetialFn = (data: AnyObject) => {
|
const getAddonDetailFn = (data: any) => {
|
||||||
appStoreShowDialog.value = true
|
appStoreShowDialog.value = true
|
||||||
appStoreInfo.value = data
|
appStoreInfo.value = data
|
||||||
}
|
}
|
||||||
|
// 更新信息
|
||||||
|
|
||||||
|
const upgradeKey = ref<string>('')
|
||||||
|
const updateInformationFn = (data: any) => {
|
||||||
|
// updateInformationDialog.value = true
|
||||||
|
|
||||||
|
upgradeKey.value = data.key
|
||||||
|
upgradeLogRef.value?.open()
|
||||||
|
}
|
||||||
// 授权
|
// 授权
|
||||||
const authCodeApproveDialog = ref(false)
|
const authCodeApproveDialog = ref(false)
|
||||||
const authinfo = ref('')
|
const authinfo = ref('')
|
||||||
@ -836,18 +878,17 @@ const saveLoading = ref(false)
|
|||||||
const authLoading = ref(true)
|
const authLoading = ref(true)
|
||||||
const checkAppMange = () => {
|
const checkAppMange = () => {
|
||||||
authLoading.value = true
|
authLoading.value = true
|
||||||
getAuthInfo()
|
getAuthInfo().then((res) => {
|
||||||
.then((res) => {
|
|
||||||
authLoading.value = false
|
authLoading.value = false
|
||||||
if (res.data.data && res.data.data.length != 0) {
|
if (res.data.data && res.data.data.length != 0) {
|
||||||
authinfo.value = res.data.data
|
authinfo.value = res.data.data
|
||||||
}
|
}
|
||||||
})
|
}).catch(() => {
|
||||||
.catch(() => {
|
|
||||||
authLoading.value = false
|
authLoading.value = false
|
||||||
authCodeApproveDialog.value = false
|
authCodeApproveDialog.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAppMange()
|
checkAppMange()
|
||||||
const authCodeApproveFn = () => {
|
const authCodeApproveFn = () => {
|
||||||
authCodeApproveDialog.value = true
|
authCodeApproveDialog.value = true
|
||||||
@ -861,12 +902,8 @@ const formRef = ref<FormInstance>()
|
|||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const formRules = reactive<FormRules>({
|
const formRules = reactive<FormRules>({
|
||||||
auth_code: [
|
auth_code: [{ required: true, message: t('authCodePlaceholder'), trigger: 'blur' }],
|
||||||
{ required: true, message: t('authCodePlaceholder'), trigger: 'blur' }
|
auth_secret: [{ required: true, message: t('authSecretPlaceholder'), trigger: 'blur' }]
|
||||||
],
|
|
||||||
auth_secret: [
|
|
||||||
{ required: true, message: t('authSecretPlaceholder'), trigger: 'blur' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const save = async (formEl: FormInstance | undefined) => {
|
const save = async (formEl: FormInstance | undefined) => {
|
||||||
@ -876,14 +913,12 @@ const save = async (formEl: FormInstance | undefined) => {
|
|||||||
if (valid) {
|
if (valid) {
|
||||||
saveLoading.value = true
|
saveLoading.value = true
|
||||||
|
|
||||||
setAuthInfo(formData)
|
setAuthInfo(formData).then(() => {
|
||||||
.then(() => {
|
|
||||||
saveLoading.value = false
|
saveLoading.value = false
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
location.reload()
|
location.reload()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
}).catch(() => {
|
||||||
.catch(() => {
|
|
||||||
saveLoading.value = false
|
saveLoading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -895,24 +930,40 @@ const goRouter = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deleteAddonFn = (key: string) => {
|
const deleteAddonFn = (key: string) => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(t('deleteAddonTips'), t('warning'), {
|
||||||
t('deleteAddonTips'),
|
|
||||||
t('warning'),
|
|
||||||
{
|
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}
|
}).then(() => {
|
||||||
).then(() => {
|
|
||||||
deleteAddonDevelop(key).then(() => {
|
deleteAddonDevelop(key).then(() => {
|
||||||
localListFn()
|
localListFn()
|
||||||
})
|
})
|
||||||
}).catch(() => { })
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionJudge = (row: any) => {
|
||||||
|
if (!row.support_version) return true
|
||||||
|
const supportVersionApp = row.support_version.split('.')
|
||||||
|
const frameworkVersionArr = frameworkVersion.value.split('.')
|
||||||
|
if (parseFloat(`${ supportVersionApp[0] }.${ supportVersionApp[1] }`) < parseFloat(`${ frameworkVersionArr[0] }.${ frameworkVersionArr[1] }`)) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let batchUpgradeApp = []
|
||||||
|
const handleSelectionChange = (e: any) => {
|
||||||
|
batchUpgradeApp = e.map(item => item.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
const batchUpgrade = () => {
|
||||||
|
if (!batchUpgradeApp.length) {
|
||||||
|
ElMessage({ message: '请先勾选要升级的插件', type: 'error', duration: 5000 })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
upgradeAddonFn(batchUpgradeApp.toString())
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
/* 多行超出隐藏 */
|
|
||||||
.multi-hidden {
|
.multi-hidden {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -922,50 +973,6 @@ const deleteAddonFn = (key: string) => {
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.demo-tabs>.el-tabs__content {
|
|
||||||
padding: 32px;
|
|
||||||
color: #6b778c;
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plug-item {
|
|
||||||
.plug-item-operate {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
border-color: var(--el-color-primary);
|
|
||||||
font-size: var(--el-font-size-extra-small);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.t-container) {
|
|
||||||
box-shadow: none !important;
|
|
||||||
|
|
||||||
.t-window {
|
|
||||||
padding: 10px 20px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-btn.active {
|
|
||||||
border-color: var(--el-color-primary);
|
|
||||||
color: #fff;
|
|
||||||
background-color: var(--el-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.plug-large {
|
|
||||||
.plug-item-operate {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
border-color: var(--el-color-primary);
|
|
||||||
font-size: var(--el-font-size-extra-small);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-text {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
-o-text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 插件安装-弹窗-表格样式
|
// 插件安装-弹窗-表格样式
|
||||||
.table-head-bg {
|
.table-head-bg {
|
||||||
background: #f5f7f9;
|
background: #f5f7f9;
|
||||||
@ -980,13 +987,10 @@ html.dark .table-head-bg {
|
|||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-store {
|
|
||||||
height: calc(100vh - 120px);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
:deep(.terminal .t-log-box span) {
|
:deep(.terminal .t-log-box span) {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.data-loading) {
|
:deep(.data-loading) {
|
||||||
.el-table__body-wrapper {
|
.el-table__body-wrapper {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<el-input class="input-width" v-model.trim="formData.continue_sign" @keyup="filterNumber($event)" :maxlength="3" clearable />
|
<el-input class="input-width" v-model.trim="formData.continue_sign" @keyup="filterNumber($event)" :maxlength="3" clearable />
|
||||||
<span class="ml-[10px]">{{ t('day') }}</span>
|
<span class="ml-[10px]">{{ t('day') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('continueSign')" >
|
<el-form-item :label="t('continueSignAward')">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div v-for="(item,index) in gifts" :key="index" class="mb-[15px]">
|
<div v-for="(item,index) in gifts" :key="index" class="mb-[15px]">
|
||||||
<component :is="item.component" v-model="formData[item.key]" ref="giftRefs" v-if="item.component" />
|
<component :is="item.component" v-model="formData[item.key]" ref="giftRefs" v-if="item.component" />
|
||||||
@ -38,6 +38,10 @@ const props = defineProps({
|
|||||||
default: () => {
|
default: () => {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
sign_period: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const emits = defineEmits(['update:modelValue'])
|
const emits = defineEmits(['update:modelValue'])
|
||||||
@ -94,6 +98,8 @@ const formRules = reactive<FormRules>({
|
|||||||
callback(t('continueSignFormatError'))
|
callback(t('continueSignFormatError'))
|
||||||
} else if (value < 2 || value > 365) {
|
} else if (value < 2 || value > 365) {
|
||||||
callback(t('continueSignBerweenDays'))
|
callback(t('continueSignBerweenDays'))
|
||||||
|
} else if (Number(value) > Number(props.sign_period)) {
|
||||||
|
callback(t('continueSignMustLessThanSignPeriod')) // 添加这个校验
|
||||||
} else{
|
} else{
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -126,13 +132,16 @@ const formRules = reactive<FormRules>({
|
|||||||
|
|
||||||
const verify = async () => {
|
const verify = async () => {
|
||||||
let verify = true
|
let verify = true
|
||||||
|
await formRef.value?.validate((valid) => {
|
||||||
|
verify = valid
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!verify) return verify
|
||||||
|
|
||||||
for (let i = 0; i < giftRefs.value.length; i++) {
|
for (let i = 0; i < giftRefs.value.length; i++) {
|
||||||
const item = giftRefs.value[i]
|
const item = giftRefs.value[i]
|
||||||
!await item.verify() && (verify = false)
|
!await item.verify() && (verify = false)
|
||||||
}
|
}
|
||||||
await formRef.value?.validate((valid) => {
|
|
||||||
verify = valid
|
|
||||||
})
|
|
||||||
return verify
|
return verify
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -150,7 +150,6 @@ const getVerifyDetailFn = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setFormData = async (row: any = null) => {
|
const setFormData = async (row: any = null) => {
|
||||||
console.log("setFormData",row);
|
|
||||||
code = row.code;
|
code = row.code;
|
||||||
getVerifyDetailFn();
|
getVerifyDetailFn();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,7 +90,7 @@
|
|||||||
|
|
||||||
<!-- 连签奖励 -->
|
<!-- 连签奖励 -->
|
||||||
<el-dialog v-model="continueSignDialog" :title="t('continueSignTitle')" width="1200px" :destroy-on-close="true" v-if="formData.is_use">
|
<el-dialog v-model="continueSignDialog" :title="t('continueSignTitle')" width="1200px" :destroy-on-close="true" v-if="formData.is_use">
|
||||||
<sign-continue ref="continueRef" v-model="continue_award" />
|
<sign-continue ref="continueRef" v-model="continue_award" :sign_period="formData.sign_period" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="continueSignDialog = false">{{ t('cancel') }}</el-button>
|
<el-button @click="continueSignDialog = false">{{ t('cancel') }}</el-button>
|
||||||
@ -141,7 +141,21 @@ const regExp: any = {
|
|||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const formRules = reactive<FormRules>({
|
const formRules = reactive<FormRules>({
|
||||||
day_award: [
|
day_award: [
|
||||||
{ required: true, message: t('daySignAwardPlaceholder'), trigger: 'change' }
|
{
|
||||||
|
required: true,
|
||||||
|
trigger: 'change',
|
||||||
|
validator: (rule: any, value: any, callback: any) => {
|
||||||
|
let isVerify = false
|
||||||
|
daySignAwardText.value.forEach(item => {
|
||||||
|
item.is_use && (isVerify = true)
|
||||||
|
})
|
||||||
|
if (!isVerify) {
|
||||||
|
callback(t('daySignAwardPlaceholder'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
sign_period:[{
|
sign_period:[{
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@ -17,8 +17,8 @@
|
|||||||
<el-table-column :label="t('memberInfo')" min-width="120">
|
<el-table-column :label="t('memberInfo')" min-width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="flex items-center cursor-pointer " @click="toMember(row.member.member_id)" v-if="row.member">
|
<div class="flex items-center cursor-pointer " @click="toMember(row.member.member_id)" v-if="row.member">
|
||||||
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" alt="">
|
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" />
|
||||||
<img class="w-[50px] h-[50px] mr-[10px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
|
<img class="w-[50px] h-[50px] mr-[10px] rounded-full" v-else src="@/app/assets/images/member_head.png" />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span>{{ row.member.nickname || '' }}</span>
|
<span>{{ row.member.nickname || '' }}</span>
|
||||||
<span>{{ row.member.mobile || '' }}</span>
|
<span>{{ row.member.mobile || '' }}</span>
|
||||||
@ -30,17 +30,16 @@
|
|||||||
<el-table-column :label="t('verifyType')" min-width="120">
|
<el-table-column :label="t('verifyType')" min-width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div v-for="(item, key) in row.verify_type_array" class="my-[3px]" :key="key">
|
<div v-for="(item, key) in row.verify_type_array" class="my-[3px]" :key="key">{{ item.verify_type_name }}</div>
|
||||||
{{ item.verify_type_name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column :label="t('createTime')" prop="create_time" min-width="120"/>
|
<el-table-column :label="t('createTime')" prop="create_time" min-width="120"/>
|
||||||
|
|
||||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="100">
|
<el-table-column :label="t('operation')" fixed="right" align="right" width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||||
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@ -57,12 +56,12 @@
|
|||||||
<el-dialog v-model="showDialog" :title="t('addVerifier')" width="500px" :destroy-on-close="true">
|
<el-dialog v-model="showDialog" :title="t('addVerifier')" width="500px" :destroy-on-close="true">
|
||||||
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="addLoading">
|
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="addLoading">
|
||||||
<el-form-item :label="t('member')" prop="member_id">
|
<el-form-item :label="t('member')" prop="member_id">
|
||||||
<el-select v-model="formData.member_id" filterable remote reserve-keyword clearable :placeholder="t('searchPlaceholder')" :remote-method="searchMember" :loading="searchLoading" class="input-width">
|
<el-select v-model="formData.member_id" filterable remote reserve-keyword clearable @focus="handleSelectFocus" :disabled="isEditMode" :placeholder="t('searchPlaceholder')" :remote-method="searchMember" :loading="searchLoading" class="input-width">
|
||||||
<el-option v-for="item in memberList" :key="item.member_id" :label="item.nickname" :value="item.member_id"/>
|
<el-option v-for="item in memberList" :key="item.member_id" :label="item.nickname" :value="item.member_id"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('verifyType')" prop="verify_type">
|
<el-form-item :label="t('verifyType')" prop="verify_type">
|
||||||
<el-select v-model="formData.verify_type" multiple collapse-tags clearable :placeholder="t('verifyTypePlaceholder')" class="input-width">
|
<el-select v-model="formData.verify_type" multiple clearable :placeholder="t('verifyTypePlaceholder')" class="input-width">
|
||||||
<el-option v-for="(item, index) in verifyTypeList" :key="index" :label="item.name" :value="index"/>
|
<el-option v-for="(item, index) in verifyTypeList" :key="index" :label="item.name" :value="index"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -79,10 +78,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref, nextTick } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { getVerifierList, deleteVerifier, addVerifier, getVerifyTypeList } from '@/app/api/verify'
|
import {
|
||||||
|
getVerifierList,
|
||||||
|
deleteVerifier,
|
||||||
|
addVerifier,
|
||||||
|
getVerifyTypeList,
|
||||||
|
getVerifyInfo,
|
||||||
|
editVerifier
|
||||||
|
} from '@/app/api/verify'
|
||||||
import { getMemberList } from '@/app/api/member'
|
import { getMemberList } from '@/app/api/member'
|
||||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
@ -95,12 +101,15 @@ const showDialog = ref(false)
|
|||||||
const addLoading = ref(false)
|
const addLoading = ref(false)
|
||||||
const formData: Record<string, any> = reactive({
|
const formData: Record<string, any> = reactive({
|
||||||
member_id: '',
|
member_id: '',
|
||||||
verify_type: '',
|
verify_type: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
member_id: [
|
member_id: [
|
||||||
{ required: true, message: t('memberIdPlaceholder'), trigger: 'blur' }
|
{ required: true, message: t('memberIdPlaceholder'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
verify_type: [
|
||||||
|
{ required: true, message: t('verifyTypePlaceholder'), trigger: 'blur' }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
@ -137,10 +146,44 @@ loadVerifierList()
|
|||||||
/**
|
/**
|
||||||
* 添加核销员表
|
* 添加核销员表
|
||||||
*/
|
*/
|
||||||
|
const isEditMode = ref(false);
|
||||||
const addEvent = () => {
|
const addEvent = () => {
|
||||||
|
isEditMode.value = false
|
||||||
|
formData.member_id = ''
|
||||||
|
formData.id = ''
|
||||||
|
formData.verify_type = ''
|
||||||
showDialog.value = true
|
showDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editEvent = async (row: any) => {
|
||||||
|
isEditMode.value = true
|
||||||
|
formData.member_id = ''
|
||||||
|
formData.verify_type = ''
|
||||||
|
memberList.value = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getVerifyInfo(row.id)
|
||||||
|
memberList.value = [{
|
||||||
|
member_id: res.data.member.member_id,
|
||||||
|
nickname: res.data.member.nickname
|
||||||
|
}]
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
formData.member_id = res.data.member.member_id
|
||||||
|
formData.verify_type = res.data.verify_type
|
||||||
|
formData.id = row.id
|
||||||
|
showDialog.value = true
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelectFocus = () => {
|
||||||
|
if (isEditMode.value && formData.member_id && memberList.value.length === 0) {
|
||||||
|
searchMember('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除核销员表
|
* 删除核销员表
|
||||||
*/
|
*/
|
||||||
@ -168,8 +211,8 @@ const addVerifiers = async (formEl: FormInstance | undefined) => {
|
|||||||
await formEl.validate(async (valid) => {
|
await formEl.validate(async (valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
addLoading.value = true
|
addLoading.value = true
|
||||||
|
const api = formData.id ? editVerifier : addVerifier
|
||||||
addVerifier(formData).then(res => {
|
api(formData).then(res => {
|
||||||
addLoading.value = false
|
addLoading.value = false
|
||||||
showDialog.value = false
|
showDialog.value = false
|
||||||
formData.member_id = ''
|
formData.member_id = ''
|
||||||
@ -193,7 +236,7 @@ const searchMember = (query: string) => {
|
|||||||
getMemberList({ keyword: query }).then(res => {
|
getMemberList({ keyword: query }).then(res => {
|
||||||
memberList.value = res.data.data
|
memberList.value = res.data.data
|
||||||
searchLoading.value = false
|
searchLoading.value = false
|
||||||
}).catch()
|
})
|
||||||
} else {
|
} else {
|
||||||
memberList.value = []
|
memberList.value = []
|
||||||
searchLoading.value = false
|
searchLoading.value = false
|
||||||
@ -207,9 +250,9 @@ const verifyTypeList = ref<any>([])
|
|||||||
const setVerifyTypeList = () => {
|
const setVerifyTypeList = () => {
|
||||||
getVerifyTypeList().then(res => {
|
getVerifyTypeList().then(res => {
|
||||||
verifyTypeList.value = res.data
|
verifyTypeList.value = res.data
|
||||||
}).catch()
|
})
|
||||||
}
|
}
|
||||||
setVerifyTypeList();
|
setVerifyTypeList()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 会员详情
|
* 会员详情
|
||||||
@ -217,7 +260,6 @@ setVerifyTypeList();
|
|||||||
const toMember = (member_id: number) => {
|
const toMember = (member_id: number) => {
|
||||||
router.push(`/member/detail?id=${ member_id }`)
|
router.push(`/member/detail?id=${ member_id }`)
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -73,14 +73,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { FormInstance } from 'element-plus'
|
import { FormInstance } from 'element-plus'
|
||||||
import { getVerifyRecord, getVerifyTypeList, getVerifierSelect } from '@/app/api/verify'
|
import { getVerifyRecord, getVerifyTypeList, getVerifierSelect } from '@/app/api/verify'
|
||||||
import verifyDetail from '@/app/views/marketing/components/verify-detail.vue'
|
import verifyDetail from '@/app/views/marketing/components/verify-detail.vue'
|
||||||
import { img } from '@/utils/common'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
|
||||||
const pageName = route.meta.title
|
const pageName = route.meta.title
|
||||||
|
|
||||||
const recordTable = reactive({
|
const recordTable = reactive({
|
||||||
|
|||||||
@ -135,7 +135,7 @@ import { t } from '@/lang'
|
|||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { getRegisterChannelType, getMemberList, getMemberLabelAll, editMemberStatus, deleteMember, getMemberLevelAll } from '@/app/api/member'
|
import { getRegisterChannelType, getMemberList, getMemberLabelAll, editMemberStatus, deleteMember, getMemberLevelAll } from '@/app/api/member'
|
||||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import AddMember from '@/app/views/member/components/add-member.vue'
|
import AddMember from '@/app/views/member/components/add-member.vue'
|
||||||
import detailMember from '@/app/views/member/components/detail-member.vue'
|
import detailMember from '@/app/views/member/components/detail-member.vue'
|
||||||
import EditMember from '@/app/views/member/components/edit-member.vue'
|
import EditMember from '@/app/views/member/components/edit-member.vue'
|
||||||
@ -205,7 +205,6 @@ const loadMemberList = (page: number = 1) => {
|
|||||||
}
|
}
|
||||||
loadMemberList()
|
loadMemberList()
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const addMemberDialog: Record<string, any> | null = ref(null)
|
const addMemberDialog: Record<string, any> | null = ref(null)
|
||||||
const editMemberDialog: Record<string, any> | null = ref(null)
|
const editMemberDialog: Record<string, any> | null = ref(null)
|
||||||
const detailMemberDialog: Record<string, any> | null = ref(null)
|
const detailMemberDialog: Record<string, any> | null = ref(null)
|
||||||
|
|||||||
@ -33,9 +33,7 @@
|
|||||||
<el-statistic :value="formData.point">
|
<el-statistic :value="formData.point">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div style="display: inline-flex; align-items: center">
|
<div style="display: inline-flex; align-items: center">
|
||||||
<span class="text-[14px]">
|
<span class="text-[14px]">{{ t('point') }}</span>
|
||||||
{{ t('point') }}
|
|
||||||
</span>
|
|
||||||
<el-tooltip effect="dark" :content="t('adjust')" placement="top">
|
<el-tooltip effect="dark" :content="t('adjust')" placement="top">
|
||||||
<el-icon @click="adjustPoint(formData)" class="ml-2 cursor-pointer" :size="12">
|
<el-icon @click="adjustPoint(formData)" class="ml-2 cursor-pointer" :size="12">
|
||||||
<EditPen color="#273CE2" />
|
<EditPen color="#273CE2" />
|
||||||
@ -64,9 +62,7 @@
|
|||||||
<el-statistic :value="formData.balance">
|
<el-statistic :value="formData.balance">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div style="display: inline-flex; align-items: center">
|
<div style="display: inline-flex; align-items: center">
|
||||||
<span class="text-[14px]">
|
<span class="text-[14px]">{{ t('balance') }}</span>
|
||||||
{{ t('balance') }}
|
|
||||||
</span>
|
|
||||||
<el-tooltip effect="dark" :content="t('adjust')" placement="top">
|
<el-tooltip effect="dark" :content="t('adjust')" placement="top">
|
||||||
<el-icon @click="adjustBalance(formData)" class="ml-2 cursor-pointer" :size="12">
|
<el-icon @click="adjustBalance(formData)" class="ml-2 cursor-pointer" :size="12">
|
||||||
<EditPen color="#273CE2" />
|
<EditPen color="#273CE2" />
|
||||||
@ -83,9 +79,7 @@
|
|||||||
<div class="statistic-footer">
|
<div class="statistic-footer">
|
||||||
<div class="footer-item text-[14px] text-secondary">
|
<div class="footer-item text-[14px] text-secondary">
|
||||||
<span>{{ t('accumulative') }}</span>
|
<span>{{ t('accumulative') }}</span>
|
||||||
<span class="red ml-1">
|
<span class="red ml-1">{{ formData.balance_get }}</span>
|
||||||
{{ formData.balance_get }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -95,9 +89,7 @@
|
|||||||
<el-statistic :value="formData.growth">
|
<el-statistic :value="formData.growth">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div style="display: inline-flex; align-items: center">
|
<div style="display: inline-flex; align-items: center">
|
||||||
<span class="text-[14px]">
|
<span class="text-[14px]">{{ t('growth') }}</span>
|
||||||
{{ t('growth') }}
|
|
||||||
</span>
|
|
||||||
<!-- <el-tooltip effect="dark" :content="t('adjust')" placement="top">-->
|
<!-- <el-tooltip effect="dark" :content="t('adjust')" placement="top">-->
|
||||||
<!-- <el-icon @click="adjustGrowth(formData)" class="ml-2 cursor-pointer" :size="12">-->
|
<!-- <el-icon @click="adjustGrowth(formData)" class="ml-2 cursor-pointer" :size="12">-->
|
||||||
<!-- <EditPen color="#273CE2" />-->
|
<!-- <EditPen color="#273CE2" />-->
|
||||||
@ -118,9 +110,7 @@
|
|||||||
<el-statistic :value="formData.money" title="New transactions today">
|
<el-statistic :value="formData.money" title="New transactions today">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div style="display: inline-flex; align-items: center">
|
<div style="display: inline-flex; align-items: center">
|
||||||
<span class="text-[14px]">
|
<span class="text-[14px]">{{ t("money") }}</span>
|
||||||
{{ t("money") }}
|
|
||||||
</span>
|
|
||||||
<el-tooltip effect="dark" :content="t('detail')" placement="top">
|
<el-tooltip effect="dark" :content="t('detail')" placement="top">
|
||||||
<el-icon @click="infoBalance(formData)" class="ml-2 cursor-pointer" :size="12">
|
<el-icon @click="infoBalance(formData)" class="ml-2 cursor-pointer" :size="12">
|
||||||
<View />
|
<View />
|
||||||
@ -132,9 +122,7 @@
|
|||||||
<div class="statistic-footer">
|
<div class="statistic-footer">
|
||||||
<div class="footer-item text-[14px] text-secondary">
|
<div class="footer-item text-[14px] text-secondary">
|
||||||
<span>{{ t('accumulative') }}</span>
|
<span>{{ t('accumulative') }}</span>
|
||||||
<span class="green ml-1">
|
<span class="green ml-1">{{ formData.money_get }}</span>
|
||||||
{{ formData.money_get }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -144,9 +132,7 @@
|
|||||||
<el-statistic :value="formData.commission" title="New transactions today">
|
<el-statistic :value="formData.commission" title="New transactions today">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div style="display: inline-flex; align-items: center ">
|
<div style="display: inline-flex; align-items: center ">
|
||||||
<span class="text-[14px]">
|
<span class="text-[14px]">{{ t("commission") }}</span>
|
||||||
{{ t("commission") }}
|
|
||||||
</span>
|
|
||||||
<el-tooltip effect="dark" :content="t('detail')" placement="top">
|
<el-tooltip effect="dark" :content="t('detail')" placement="top">
|
||||||
<el-icon @click="infoCommission(formData)" class="ml-2 cursor-pointer" :size="12">
|
<el-icon @click="infoCommission(formData)" class="ml-2 cursor-pointer" :size="12">
|
||||||
<View />
|
<View />
|
||||||
@ -158,9 +144,7 @@
|
|||||||
<div class="statistic-footer">
|
<div class="statistic-footer">
|
||||||
<div class="footer-item text-[14px] text-secondary">
|
<div class="footer-item text-[14px] text-secondary">
|
||||||
<span>{{ t('accumulative') }}</span>
|
<span>{{ t('accumulative') }}</span>
|
||||||
<span class="green ml-1">
|
<span class="green ml-1">{{ formData.commission_get }}</span>
|
||||||
{{ formData.commission_get }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -172,22 +156,16 @@
|
|||||||
<el-card class="box-card !border-none" shadow="never">
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
<div class="flex items-center mt-[15px]">
|
<div class="flex items-center mt-[15px]">
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('urserName') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('urserName') }}</span>
|
||||||
<span class="text-[14px] text-[#666666]">
|
<span class="text-[14px] text-[#666666]">{{ formData.username || t('notAvailable') }}</span>
|
||||||
{{ formData.username || t('notAvailable') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mt-[15px]">
|
<div class="flex items-center mt-[15px]">
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('nickname') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('nickname') }}</span>
|
||||||
<span class="text-[14px] text-[#666666]">
|
<span class="text-[14px] text-[#666666]">{{ formData.nickname || t('notAvailable') }}</span>
|
||||||
{{ formData.nickname || t('notAvailable') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center mt-[15px]">
|
<div class="flex items-center mt-[15px]">
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('mobile') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('mobile') }}</span>
|
||||||
<span class="text-[14px] text-[#666666]">
|
<span class="text-[14px] text-[#666666]">{{ formData.mobile || t('notAvailable') }}</span>
|
||||||
{{ formData.mobile || t('notAvailable') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mt-[15px]">
|
<div class="flex items-center mt-[15px]">
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('memberLevel') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('memberLevel') }}</span>
|
||||||
@ -276,12 +254,10 @@ import { img } from '@/utils/common'
|
|||||||
import PointEdit from '@/app/views/member/components/member-point-edit.vue'
|
import PointEdit from '@/app/views/member/components/member-point-edit.vue'
|
||||||
import BalanceEdit from '@/app/views/member/components/member-balance-edit.vue'
|
import BalanceEdit from '@/app/views/member/components/member-balance-edit.vue'
|
||||||
import EditMember from '@/app/views/member/components/edit-member.vue'
|
import EditMember from '@/app/views/member/components/edit-member.vue'
|
||||||
import useAppStore from '@/stores/modules/app'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const pageName = route.meta.title
|
const pageName = route.meta.title
|
||||||
|
|
||||||
const appStore = useAppStore()
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
|
||||||
// 获取会员信息
|
// 获取会员信息
|
||||||
|
|||||||
@ -11,8 +11,12 @@
|
|||||||
<el-form label-width="80px" class="px-[10px]">
|
<el-form label-width="80px" class="px-[10px]">
|
||||||
<el-form-item :label="t('imgShape')">
|
<el-form-item :label="t('imgShape')">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] border-solid border-[1px] border-transparent rounded-[50%]" :class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'circle'}" @click="imgShapeChangeFn('circle')"></div>
|
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] border-solid border-[1px] border-transparent rounded-[50%]"
|
||||||
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] ml-[25px] border-solid border-[1px] border-transparent" :class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'normal'}" @click="imgShapeChangeFn('normal')"></div>
|
:class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'circle'}"
|
||||||
|
@click="imgShapeChangeFn('circle')"></div>
|
||||||
|
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] ml-[25px] border-solid border-[1px] border-transparent"
|
||||||
|
:class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'normal'}"
|
||||||
|
@click="imgShapeChangeFn('normal')"></div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { watch, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import usePosterStore from '@/stores/modules/poster'
|
import usePosterStore from '@/stores/modules/poster'
|
||||||
|
|
||||||
|
|||||||
@ -82,8 +82,7 @@
|
|||||||
:style="previewIframeStyle(item)"
|
:style="previewIframeStyle(item)"
|
||||||
:class="{ 'selected' : posterStore.currentIndex == index }"
|
:class="{ 'selected' : posterStore.currentIndex == index }"
|
||||||
@mousedown="posterStore.mouseDown($event,item.id,index)"
|
@mousedown="posterStore.mouseDown($event,item.id,index)"
|
||||||
@click.stop="posterStore.changeCurrentIndex(index,item)"
|
@click.stop="posterStore.changeCurrentIndex(index,item)">
|
||||||
>
|
|
||||||
<component :is="modules['preview-' + item.path]" :value="item"/>
|
<component :is="modules['preview-' + item.path]" :value="item"/>
|
||||||
<span class="box1" @mousedown.stop="posterStore.resizeMouseDown($event,item, index)"></span>
|
<span class="box1" @mousedown.stop="posterStore.resizeMouseDown($event,item, index)"></span>
|
||||||
<span class="box2" @mousedown.stop="posterStore.resizeMouseDown($event,item, index)"></span>
|
<span class="box2" @mousedown.stop="posterStore.resizeMouseDown($event,item, index)"></span>
|
||||||
@ -299,7 +298,6 @@ const previewIframeStyle = (data: any)=>{
|
|||||||
default:
|
default:
|
||||||
style.left = data.x + 'px'
|
style.left = data.x + 'px'
|
||||||
}
|
}
|
||||||
// console.log(data.x,data.y)
|
|
||||||
return style
|
return style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
|
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
|
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef"
|
||||||
|
v-loading="loading">
|
||||||
<el-card class="box-card !border-none" shadow="never">
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
|
||||||
<h3 class="panel-title !text-sm">{{ t('printerSet') }}</h3>
|
<h3 class="panel-title !text-sm">{{ t('printerSet') }}</h3>
|
||||||
@ -83,8 +84,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<template v-for="childItem in item.condition">
|
<template v-for="childItem in item.condition">
|
||||||
<div class="w-[300px] px-[12px] flex-1" v-if="childItem.type == 'checkbox'">
|
<div class="w-[300px] px-[12px] flex-1" v-if="childItem.type == 'checkbox'">
|
||||||
<el-checkbox-group v-model="formData.value[item.key]['trigger_' + triggerKey][childItem.key]">
|
<el-checkbox-group
|
||||||
<el-checkbox v-for="(checkboxItem, index) in childItem.list" :label="checkboxItem.value" :key="index">{{ checkboxItem.name }}</el-checkbox>
|
v-model="formData.value[item.key]['trigger_' + triggerKey][childItem.key]">
|
||||||
|
<el-checkbox v-for="(checkboxItem, index) in childItem.list" :label="checkboxItem.value" :key="index">{{ checkboxItem.name }}
|
||||||
|
</el-checkbox>
|
||||||
</el-checkbox-group>
|
</el-checkbox-group>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -118,7 +121,14 @@
|
|||||||
import { FormInstance, ElMessage } from 'element-plus'
|
import { FormInstance, ElMessage } from 'element-plus'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { deepClone } from '@/utils/common';
|
import { deepClone } from '@/utils/common';
|
||||||
import { addPrinter, editPrinter,getPrinterInfo,getPrinterType,getPrinterBrand,getPrinterTemplateList } from '@/app/api/printer'
|
import {
|
||||||
|
addPrinter,
|
||||||
|
editPrinter,
|
||||||
|
getPrinterInfo,
|
||||||
|
getPrinterType,
|
||||||
|
getPrinterBrand,
|
||||||
|
getPrinterTemplateList
|
||||||
|
} from '@/app/api/printer'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
@ -4,9 +4,7 @@
|
|||||||
|
|
||||||
<div class="flex justify-between items-center mb-[5px]">
|
<div class="flex justify-between items-center mb-[5px]">
|
||||||
<span class="text-lg">{{pageName}}</span>
|
<span class="text-lg">{{pageName}}</span>
|
||||||
<el-button type="primary" @click="addEvent">
|
<el-button type="primary" @click="addEvent">{{ t('addPrinter') }}</el-button>
|
||||||
{{ t('addPrinter') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-tabs class="demo-tabs" model-value="/printer/list" @tab-change="handleClick">
|
<el-tabs class="demo-tabs" model-value="/printer/list" @tab-change="handleClick">
|
||||||
@ -73,6 +71,7 @@ import { t } from '@/lang'
|
|||||||
import { getPrinterPageList, modifyPrinterStatus, deletePrinter,refreshPrinterToken,testPrint } from '@/app/api/printer'
|
import { getPrinterPageList, modifyPrinterStatus, deletePrinter,refreshPrinterToken,testPrint } from '@/app/api/printer'
|
||||||
import { ElMessageBox,FormInstance } from 'element-plus'
|
import { ElMessageBox,FormInstance } from 'element-plus'
|
||||||
import { useRoute,useRouter } from 'vue-router'
|
import { useRoute,useRouter } from 'vue-router'
|
||||||
|
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -111,12 +110,13 @@ const loadPrinterList = (page: number = 1) => {
|
|||||||
printerTable.loading = false
|
printerTable.loading = false
|
||||||
printerTable.data = res.data.data
|
printerTable.data = res.data.data
|
||||||
printerTable.total = res.data.total
|
printerTable.total = res.data.total
|
||||||
|
setTablePageStorage(printerTable.page, printerTable.limit, printerTable.searchParam);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
printerTable.loading = false
|
printerTable.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPrinterList()
|
loadPrinterList(getTablePageStorage(printerTable.searchParam).page)
|
||||||
|
|
||||||
const isRepeat = ref(false)
|
const isRepeat = ref(false)
|
||||||
|
|
||||||
|
|||||||
@ -33,10 +33,15 @@
|
|||||||
<h4 class="panel-title !text-sm">{{ item.title }}</h4>
|
<h4 class="panel-title !text-sm">{{ item.title }}</h4>
|
||||||
<div v-for="(childItem,index) in item.list" :key="childItem.key" class="ml-[30px]" :style="{ 'margin-bottom' : item.list.length == (index + 1) ? '0' : '20px' }">
|
<div v-for="(childItem,index) in item.list" :key="childItem.key" class="ml-[30px]" :style="{ 'margin-bottom' : item.list.length == (index + 1) ? '0' : '20px' }">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<el-checkbox v-model="formData.value[item.key][childItem.key].status" v-if="childItem.label" :label="childItem.label" :value="childItem.status" :true-value="1" :false-value="0" class="w-[180px] mr-[10px]" :disabled="childItem.disabled" />
|
<el-checkbox v-model="formData.value[item.key][childItem.key].status"
|
||||||
|
v-if="childItem.label" :label="childItem.label"
|
||||||
|
:value="childItem.status" :true-value="1" :false-value="0"
|
||||||
|
class="w-[180px] mr-[10px]" :disabled="childItem.disabled" />
|
||||||
|
|
||||||
<template v-if="childItem.type == 'input'">
|
<template v-if="childItem.type == 'input'">
|
||||||
<el-input v-model.trim="formData.value[item.key][childItem.key].value" clearable :placeholder="'请输入' + (childItem.placeholder ? childItem.placeholder : childItem.label)" class="input-width mr-[30px]" maxlength="32" />
|
<el-input v-model.trim="formData.value[item.key][childItem.key].value" clearable
|
||||||
|
:placeholder="'请输入' + (childItem.placeholder ? childItem.placeholder : childItem.label)"
|
||||||
|
class="input-width mr-[30px]" maxlength="32" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="childItem.type == 'checkbox'">
|
<template v-if="childItem.type == 'checkbox'">
|
||||||
|
|||||||
@ -68,6 +68,7 @@ import { t } from '@/lang'
|
|||||||
import { getPrinterTemplatePageList, deletePrinterTemplate,getPrinterType } from '@/app/api/printer'
|
import { getPrinterTemplatePageList, deletePrinterTemplate,getPrinterType } from '@/app/api/printer'
|
||||||
import { ElMessageBox,FormInstance } from 'element-plus'
|
import { ElMessageBox,FormInstance } from 'element-plus'
|
||||||
import { useRoute,useRouter } from 'vue-router'
|
import { useRoute,useRouter } from 'vue-router'
|
||||||
|
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -113,11 +114,12 @@ const loadPrinterTemplateList = (page: number = 1) => {
|
|||||||
printerTemplateTable.loading = false
|
printerTemplateTable.loading = false
|
||||||
printerTemplateTable.data = res.data.data
|
printerTemplateTable.data = res.data.data
|
||||||
printerTemplateTable.total = res.data.total
|
printerTemplateTable.total = res.data.total
|
||||||
|
setTablePageStorage(printerTemplateTable.page, printerTemplateTable.limit, printerTemplateTable.searchParam);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
printerTemplateTable.loading = false
|
printerTemplateTable.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
loadPrinterTemplateList()
|
loadPrinterTemplateList(getTablePageStorage(printerTemplateTable.searchParam).page)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加小票打印模板
|
* 添加小票打印模板
|
||||||
|
|||||||
@ -154,7 +154,6 @@ const cancel = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setFormData = async (data: any = null) => {
|
const setFormData = async (data: any = null) => {
|
||||||
console.log(data)
|
|
||||||
initData.value = cloneDeep(data)
|
initData.value = cloneDeep(data)
|
||||||
loading.value = true
|
loading.value = true
|
||||||
Object.assign(formData, initialFormData)
|
Object.assign(formData, initialFormData)
|
||||||
|
|||||||
@ -81,11 +81,7 @@ setFormData()
|
|||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const formRules = reactive<FormRules>({
|
const formRules = reactive<FormRules>({})
|
||||||
site_name: [
|
|
||||||
{ required: true, message: t('siteNamePlaceholder'), trigger: 'blur' }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存
|
* 保存
|
||||||
|
|||||||
@ -83,7 +83,7 @@ import { t } from '@/lang'
|
|||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { getExportStatusList, getExportKeyList, getExportList, deleteExport } from '@/app/api/sys'
|
import { getExportStatusList, getExportKeyList, getExportList, deleteExport } from '@/app/api/sys'
|
||||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const pageName = route.meta.title
|
const pageName = route.meta.title
|
||||||
@ -141,8 +141,6 @@ const loadExportList = (page: number = 1) => {
|
|||||||
}
|
}
|
||||||
loadExportList()
|
loadExportList()
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载导出报表
|
* 下载导出报表
|
||||||
*/
|
*/
|
||||||
@ -168,7 +166,6 @@ const deleteEvent = (id: number) => {
|
|||||||
).then(() => {
|
).then(() => {
|
||||||
deleteExport(id).then(() => {
|
deleteExport(id).then(() => {
|
||||||
loadExportList()
|
loadExportList()
|
||||||
}).catch(() => {
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,12 +65,8 @@ import { reactive, ref, computed } from 'vue'
|
|||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getLoginConfig, setLoginConfig } from '@/app/api/member'
|
import { getLoginConfig, setLoginConfig } from '@/app/api/member'
|
||||||
import { FormInstance } from 'element-plus'
|
import { FormInstance } from 'element-plus'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const pageName = route.meta.title
|
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const ruleFormRef = ref<FormInstance>()
|
const ruleFormRef = ref<FormInstance>()
|
||||||
const formData:any = reactive({
|
const formData:any = reactive({
|
||||||
|
|||||||
@ -85,10 +85,6 @@ import { getNoticeList } from '@/app/api/notice'
|
|||||||
import Sms from '@/app/views/setting/components/notice-sms.vue'
|
import Sms from '@/app/views/setting/components/notice-sms.vue'
|
||||||
import Wechat from '@/app/views/setting/components/notice-wechat.vue'
|
import Wechat from '@/app/views/setting/components/notice-wechat.vue'
|
||||||
import Weapp from '@/app/views/setting/components/notice-weapp.vue'
|
import Weapp from '@/app/views/setting/components/notice-weapp.vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const pageName = route.meta.title
|
|
||||||
|
|
||||||
const smsDialog : Record<string, any> | null = ref(null)
|
const smsDialog : Record<string, any> | null = ref(null)
|
||||||
const wechatDialog : Record<string, any> | null = ref(null)
|
const wechatDialog : Record<string, any> | null = ref(null)
|
||||||
@ -153,9 +149,17 @@ loadNoticeList()
|
|||||||
|
|
||||||
const setNotice = (data : any, type : string) => {
|
const setNotice = (data : any, type : string) => {
|
||||||
data.type = type
|
data.type = type
|
||||||
eval('data.status=data.is_' + type)
|
data.status = data['is_' + type]
|
||||||
eval(type + 'Dialog.value.setFormData(data)')
|
if (type === 'sms') {
|
||||||
eval(type + 'Dialog.value.showDialog = true;')
|
smsDialog.value.setFormData(data)
|
||||||
|
smsDialog.value.showDialog = true
|
||||||
|
} else if (type === 'wechat') {
|
||||||
|
wechatDialog.value.setFormData(data)
|
||||||
|
wechatDialog.value.showDialog = true
|
||||||
|
} else if (type === 'weapp') {
|
||||||
|
weappDialog.value.setFormData(data)
|
||||||
|
weappDialog.value.showDialog = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -134,7 +134,6 @@ const setConfigInfo = (data:any) => {
|
|||||||
element.config = data.config
|
element.config = data.config
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
console.log(payConfigData.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化配置信息
|
// 初始化配置信息
|
||||||
@ -157,10 +156,6 @@ const enablePaymentMode = async (data: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SortableEvt extends SortableEvent {
|
|
||||||
originalEvent?: DragEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拖动
|
// 拖动
|
||||||
const fieldBoxRefs = ref<any>([])
|
const fieldBoxRefs = ref<any>([])
|
||||||
watch(isEdit, (newValue, oldValue) => {
|
watch(isEdit, (newValue, oldValue) => {
|
||||||
|
|||||||
@ -142,7 +142,6 @@ const save = async (formEl: FormInstance | undefined) => {
|
|||||||
|
|
||||||
setWebsite(formData).then(() => {
|
setWebsite(formData).then(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
appType.value == 'admin' ? useSystemStore().getWebsiteInfo() : useUserStore().getSiteInfo()
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
})
|
})
|
||||||
|
|||||||
@ -40,6 +40,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-tip">{{ t('mchPublicCertPathTips') }}</div>
|
<div class="form-tip">{{ t('mchPublicCertPathTips') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('wechatpayPublicCert')" prop="wechatpay_config.wechat_public_cert_path">
|
||||||
|
<div class="input-width">
|
||||||
|
<upload-file v-model="formData.wechatpay_config.wechat_public_cert_path" api="sys/document/wechat" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="t('wechatpayPublicCertId')" prop="wechatpay_config.wechat_public_cert_id">
|
||||||
|
<div class="input-width">
|
||||||
|
<el-input v-model.trim="formData.wechatpay_config.wechat_public_cert_id" placeholder="" class="input-width" show-word-limit clearable />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- <el-card class="box-card mt-[15px] !border-none" shadow="never">
|
<!-- <el-card class="box-card mt-[15px] !border-none" shadow="never">
|
||||||
@ -98,7 +109,9 @@ const initialFormData = {
|
|||||||
mch_id: '',
|
mch_id: '',
|
||||||
mch_secret_key: '',
|
mch_secret_key: '',
|
||||||
mch_secret_cert: '',
|
mch_secret_cert: '',
|
||||||
mch_public_cert_path: ''
|
mch_public_cert_path: '',
|
||||||
|
wechat_public_cert_path: '',
|
||||||
|
wechat_public_cert_id: ''
|
||||||
},
|
},
|
||||||
alipay_config: {
|
alipay_config: {
|
||||||
app_secret_cert: '',
|
app_secret_cert: '',
|
||||||
|
|||||||
@ -205,7 +205,6 @@ const getAddonDevelopCheckFn = (key: any) => {
|
|||||||
// autofocus: false,
|
// autofocus: false,
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
callback: (action: any) => {
|
callback: (action: any) => {
|
||||||
console.log(action)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
796
admin/src/app/views/tools/backup_records.vue
Normal file
796
admin/src/app/views/tools/backup_records.vue
Normal file
@ -0,0 +1,796 @@
|
|||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="main-container">
|
||||||
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-page-title">{{ pageName }}</span>
|
||||||
|
<el-button type="primary" @click="manualBackupEvent">
|
||||||
|
{{ t('manualBackup') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||||
|
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
|
||||||
|
<el-form-item :label="t('content')" prop="content">
|
||||||
|
<el-input v-model.trim="tableData.searchParam.content" :placeholder="t('contentPlaceholder')" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="loadList()">{{ t('search') }}</el-button>
|
||||||
|
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<div class="mb-[10px] flex items-center">
|
||||||
|
<el-button @click="batchDelete" size="small">{{ t('batchDelete') }}</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="tableData.data" size="large" v-loading="tableData.loading" ref="tableRef" @selection-change="handleSelectionChange">
|
||||||
|
|
||||||
|
<template #empty>
|
||||||
|
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="id" :label="t('id')" width="120" />
|
||||||
|
<el-table-column prop="content" :label="t('content')" width="120" />
|
||||||
|
<el-table-column prop="version" :label="t('currentVersion')" width="120" />
|
||||||
|
<el-table-column prop="backup_dir" :label="t('backupDir')" width="220" />
|
||||||
|
<el-table-column prop="complete_time" :label="t('completeTime')" width="220" />
|
||||||
|
<el-table-column prop="remark" :label="t('remark')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.remark" class="multi-hidden">{{ row.remark }}</span>
|
||||||
|
<span v-else>{{ t('remarkEmpty') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="t('operation')" align="right" fixed="right" width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link @click="remarkEvent(row)">{{ t('remark') }}</el-button>
|
||||||
|
<el-button type="primary" link @click="restoreEvent(row)">{{ t('restore') }}</el-button>
|
||||||
|
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="mt-[16px] flex justify-end">
|
||||||
|
<el-pagination v-model:current-page="tableData.page"
|
||||||
|
v-model:page-size="tableData.limit" layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="tableData.total" @size-change="loadList()"
|
||||||
|
@current-change="loadList" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="showDialog" :title="iSBackupRecovery == 1 ? t('manualBackupTitle') : t('restoreTitle')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="true" :before-close="dialogClose">
|
||||||
|
|
||||||
|
<el-steps :active="numberOfSteps" align-center class="number-of-steps" finish-status="success" process-status="process">
|
||||||
|
<template v-if="iSBackupRecovery == 1">
|
||||||
|
<!-- 手动备份 -->
|
||||||
|
<el-step :title="t('testDirectoryPermissions')" />
|
||||||
|
<el-step :title="t('startBackUp')" />
|
||||||
|
<el-step :title="t('backUpEnd')" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<!-- 恢复 -->
|
||||||
|
<el-step :title="t('testDirectoryPermissions')" />
|
||||||
|
<el-step :title="t('startUpgrade')" />
|
||||||
|
<el-step :title="t('upgradeEnd')" />
|
||||||
|
</template>
|
||||||
|
</el-steps>
|
||||||
|
|
||||||
|
<div class="h-[400px]" style="overflow: auto">
|
||||||
|
|
||||||
|
<!-- 检测目录权限 -->
|
||||||
|
<div class="flex flex-col" v-show="active == 'check'">
|
||||||
|
<el-scrollbar>
|
||||||
|
<div class="bg-[#fff] my-3">
|
||||||
|
<div class="px-[20px] pt-[10px] text-[14px] el-table">
|
||||||
|
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||||
|
<el-col :span="18">
|
||||||
|
<span>{{ t("upgrade.path") }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="3">
|
||||||
|
<span>{{ t("upgrade.demand") }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="3">
|
||||||
|
<span>{{ t("status") }}</span>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div style="height: calc(300px); overflow: auto" v-if="upgradeCheck && upgradeCheck.dir">
|
||||||
|
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_readable">
|
||||||
|
<el-col :span="18">
|
||||||
|
<span>{{ item.dir }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="3">
|
||||||
|
<span :class="{ 'mx-[10px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">{{ t("upgrade.readable") }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="3">
|
||||||
|
<span v-if="item.status" :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
<span v-else :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||||
|
<el-icon color="red">
|
||||||
|
<CloseBold />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_write">
|
||||||
|
<el-col :span="18">
|
||||||
|
<span>{{ item.dir }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="3">
|
||||||
|
<span :class="{ 'mx-[10px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">{{ t("upgrade.write") }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="3">
|
||||||
|
<span v-if="item.status" :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||||
|
<el-icon color="green">
|
||||||
|
<Select />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
<span v-else :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||||
|
<el-icon color="red">
|
||||||
|
<CloseBold />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div v-loading="true" style="height: calc(300px); overflow: auto"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 执行任务 -->
|
||||||
|
<div class="h-[370px] mt-[30px]" v-if="active == 'execute'">
|
||||||
|
<terminal ref="terminalRef" context="" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 完成 -->
|
||||||
|
<div class="mt-[50px]" v-if="active == 'complete'">
|
||||||
|
<el-result icon="success" :title="iSBackupRecovery == 1 ? t('backupCompleteTips') : t('restoreCompleteTips')"></el-result>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer" v-if="active == 'check'">
|
||||||
|
<!-- 手动备份 -->
|
||||||
|
<el-button v-if="iSBackupRecovery == 1" type="primary" :loading="uploading" :disabled="isPass" @click="manualBackupFn()">{{ t("nextStep") }}</el-button>
|
||||||
|
|
||||||
|
<!-- 恢复 -->
|
||||||
|
<el-button v-else type="primary" :loading="uploading" :disabled="isPass" @click="restoreUpgradeBackupFn(currentId)">{{ t("nextStep") }}</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="showRemarkDialog" :title="t('remark')" width="460px" :destroy-on-close="true">
|
||||||
|
<el-form :model="formData" ref="formRef" class="page-form" v-loading="remarkLoading">
|
||||||
|
<el-form-item class="mb-0">
|
||||||
|
<el-input v-model.trim="formData.remark" :rows="5" type="textarea" maxlength="200" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showRemarkDialog = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button type="primary" :loading="remarkLoading" @click="modifyRemarkFn()">{{ t('confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, nextTick, watch, h } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { checkDirExist, checkPermission, getBackupRecords, restoreUpgradeBackup, deleteRecords, modifyBackupRemark, manualBackup, performBackupTasks, performRecoveryTasks } from '@/app/api/upgrade'
|
||||||
|
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
||||||
|
import 'vue-web-terminal/lib/theme/dark.css'
|
||||||
|
import { AnyObject } from '@/types/global'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const pageName = route.meta.title
|
||||||
|
const searchFormRef = ref<FormInstance>()
|
||||||
|
const multipleSelection: any = ref([]) // 选中数据
|
||||||
|
const tableRef = ref()
|
||||||
|
const repeat = ref(false)
|
||||||
|
const cloudBuildTask = ref<null | AnyObject>(null)
|
||||||
|
const tableData: any = reactive({
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: true,
|
||||||
|
data: [],
|
||||||
|
searchParam: {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const showDialog: any = ref<boolean>(false)
|
||||||
|
const active = ref('check')
|
||||||
|
const interrupt: any = ref(false) // 是否中断
|
||||||
|
const upgradeCheck = ref<null | AnyObject>(null)
|
||||||
|
const terminalRef: any = ref(null)
|
||||||
|
const cloudBuildLog: any = []
|
||||||
|
let notificationEl: any = null
|
||||||
|
const isPass: any = ref(false)
|
||||||
|
const uploading: any = ref(false)
|
||||||
|
const numberOfSteps = ref(0)
|
||||||
|
const currentId: any = ref(0)
|
||||||
|
let backupContents = []
|
||||||
|
let restoreContents = []
|
||||||
|
|
||||||
|
|
||||||
|
const resetForm = (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
|
||||||
|
formEl.resetFields()
|
||||||
|
loadList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听表格单行选中
|
||||||
|
const handleSelectionChange = (val: []) => {
|
||||||
|
multipleSelection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取列表
|
||||||
|
*/
|
||||||
|
const loadList = (page: number = 1) => {
|
||||||
|
tableData.loading = true
|
||||||
|
tableData.page = page
|
||||||
|
getBackupRecords({
|
||||||
|
page: tableData.page,
|
||||||
|
limit: tableData.limit,
|
||||||
|
...tableData.searchParam
|
||||||
|
}).then(res => {
|
||||||
|
tableData.loading = false
|
||||||
|
tableData.data = res.data.data
|
||||||
|
tableData.total = res.data.total
|
||||||
|
}).catch(() => {
|
||||||
|
tableData.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadList()
|
||||||
|
const iSBackupRecovery = ref(0) // 1:手动备份 2:恢复
|
||||||
|
|
||||||
|
// 手动备份
|
||||||
|
const manualBackupEvent = () => {
|
||||||
|
ElMessageBox.confirm(t('manualBackupTips'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
// if (repeat.value) return
|
||||||
|
// repeat.value = true
|
||||||
|
backupContents = []
|
||||||
|
iSBackupRecovery.value = 1
|
||||||
|
showDialog.value = true
|
||||||
|
uploading.value = true
|
||||||
|
active.value = 'check'
|
||||||
|
interrupt.value = false
|
||||||
|
checkPermissionFn()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 进入执行备份
|
||||||
|
const manualBackupFn = (task: any = '') => {
|
||||||
|
// 执行一半,中途取消,拦截执行
|
||||||
|
if (interrupt.value) return
|
||||||
|
|
||||||
|
if (task == '') {
|
||||||
|
numberOfSteps.value = 1
|
||||||
|
active.value = 'execute'
|
||||||
|
}
|
||||||
|
manualBackup({ task }).then((res: any) => {
|
||||||
|
const data = res.data
|
||||||
|
if (task == '') {
|
||||||
|
terminalRef.value.execute('clear')
|
||||||
|
terminalRef.value.execute('开始执行')
|
||||||
|
}
|
||||||
|
if (data.content && !backupContents.includes(data.content)) {
|
||||||
|
backupContents.push(data.content)
|
||||||
|
terminalRef.value.pushMessage({ content: `${ data.content }` })
|
||||||
|
}
|
||||||
|
if (data.task == 'end') {
|
||||||
|
numberOfSteps.value = 2
|
||||||
|
setTimeout(() => {
|
||||||
|
numberOfSteps.value = 3
|
||||||
|
active.value = 'complete'
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 1500)
|
||||||
|
} else if (data.task == 'fail') {
|
||||||
|
// 恢复失败
|
||||||
|
setTimeout(() => {
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
// 延迟2秒请求,等待恢复数据加载完成
|
||||||
|
setTimeout(() => {
|
||||||
|
manualBackupFn(data.task)
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
repeat.value = false
|
||||||
|
tableData.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询备份任务
|
||||||
|
*/
|
||||||
|
const getCloudBuildTaskFn = () => {
|
||||||
|
performBackupTasks({}).then(({ data }) => {
|
||||||
|
if (!data) return
|
||||||
|
|
||||||
|
cloudBuildTask.value = data
|
||||||
|
|
||||||
|
if (!showDialog.value && data.data && data.data.length > 0) {
|
||||||
|
showElNotification()
|
||||||
|
}
|
||||||
|
}).catch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注释获取任务 后续改
|
||||||
|
// getCloudBuildTaskFn()
|
||||||
|
/**
|
||||||
|
* 备份中任务提示
|
||||||
|
*/
|
||||||
|
const showElNotification = () => {
|
||||||
|
notificationEl = ElNotification.success({
|
||||||
|
title: t('warning'),
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
message: h('div', {}, [
|
||||||
|
t('cloudbuild.executingTips'),
|
||||||
|
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('cloudbuild.clickView')])
|
||||||
|
]),
|
||||||
|
duration: 0,
|
||||||
|
showClose: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击进入备份窗口
|
||||||
|
const elNotificationClick = () => {
|
||||||
|
iSBackupRecovery.value = 1
|
||||||
|
showDialog.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
notificationEl && notificationEl.close()
|
||||||
|
terminalRef.value.execute('clear')
|
||||||
|
terminalRef.value.execute('开始执行')
|
||||||
|
getCloudBuildLogFn()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 窗口list展示
|
||||||
|
const getCloudBuildLogFn = () => {
|
||||||
|
performBackupTasks({}).then(({ data }) => {
|
||||||
|
if (!data) return
|
||||||
|
cloudBuildTask.value = data
|
||||||
|
cloudBuildTask.value.data.forEach(item => {
|
||||||
|
if (!cloudBuildLog.includes(item.content)) {
|
||||||
|
terminalRef.value.pushMessage({ content: `${item.content}` })
|
||||||
|
cloudBuildLog.push(item.content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const lastTask = data.data[data.data.length - 1].task
|
||||||
|
if (lastTask === 'end' || data.data.length == 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
active.value = 'complete'
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 1500)
|
||||||
|
} else if (lastTask === 'fail') {
|
||||||
|
// 恢复失败
|
||||||
|
setTimeout(() => {
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
getCloudBuildLogFn()
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复备份
|
||||||
|
const restoreEvent = (data: any) => {
|
||||||
|
ElMessageBox.confirm(t('restoreTips'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
// if (repeat.value) return
|
||||||
|
// repeat.value = true
|
||||||
|
restoreContents = []
|
||||||
|
iSBackupRecovery.value = 2
|
||||||
|
currentId.value = data.id
|
||||||
|
active.value = 'check'
|
||||||
|
interrupt.value = false
|
||||||
|
checkDirExistFn(data.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测目录是否存在
|
||||||
|
const checkDirExistFn = (id: any) => {
|
||||||
|
checkDirExist({ id }).then(({ data }) => {
|
||||||
|
if (data) {
|
||||||
|
showDialog.value = true
|
||||||
|
uploading.value = true
|
||||||
|
checkPermissionFn()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测目录权限
|
||||||
|
const checkPermissionFn = () => {
|
||||||
|
checkPermission({}).then(({ data }) => {
|
||||||
|
upgradeCheck.value = data
|
||||||
|
isPass.value = !data.is_pass
|
||||||
|
uploading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行恢复备份
|
||||||
|
const restoreUpgradeBackupFn = (id: any, task: any = '') => {
|
||||||
|
// 执行一半,中途取消,拦截执行
|
||||||
|
if (interrupt.value) return
|
||||||
|
if (task == '') {
|
||||||
|
numberOfSteps.value = 1
|
||||||
|
active.value = 'execute'
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreUpgradeBackup({
|
||||||
|
id,
|
||||||
|
task
|
||||||
|
}).then((res: any) => {
|
||||||
|
const data = res.data
|
||||||
|
if (task == '') {
|
||||||
|
uploading.value = false
|
||||||
|
terminalRef.value.execute('clear')
|
||||||
|
terminalRef.value.execute('开始执行')
|
||||||
|
}
|
||||||
|
if (data.content && !restoreContents.includes(data.content)) {
|
||||||
|
restoreContents.push(data.content)
|
||||||
|
terminalRef.value.pushMessage({ content: `${ data.content }` })
|
||||||
|
}
|
||||||
|
if (data.task == 'end') {
|
||||||
|
numberOfSteps.value = 2
|
||||||
|
setTimeout(() => {
|
||||||
|
numberOfSteps.value = 3
|
||||||
|
active.value = 'complete'
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 1500)
|
||||||
|
} else if (data.task == 'fail') {
|
||||||
|
// 恢复失败
|
||||||
|
setTimeout(() => {
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
// 延迟2秒请求,等待恢复数据加载完成
|
||||||
|
setTimeout(() => {
|
||||||
|
restoreContents = []
|
||||||
|
restoreUpgradeBackupFn(id, data.task)
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
repeat.value = false
|
||||||
|
tableData.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询恢复任务
|
||||||
|
*/
|
||||||
|
const resumeUpgradeTasks = () => {
|
||||||
|
performRecoveryTasks({}).then(({ data }) => {
|
||||||
|
if (!data) return
|
||||||
|
|
||||||
|
cloudBuildTask.value = data
|
||||||
|
|
||||||
|
if (!showDialog.value && data.data && data.data.length > 0) {
|
||||||
|
recoveryTaskPrompt()
|
||||||
|
}
|
||||||
|
}).catch()
|
||||||
|
}
|
||||||
|
// 注释获取任务 后续改
|
||||||
|
// resumeUpgradeTasks()
|
||||||
|
/**
|
||||||
|
* 恢复中任务提示
|
||||||
|
*/
|
||||||
|
|
||||||
|
const recoveryTaskPrompt = () => {
|
||||||
|
notificationEl = ElNotification.success({
|
||||||
|
title: t('warning'),
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
message: h('div', {}, [
|
||||||
|
t('cloudbuild.executingTips'),
|
||||||
|
h('span', { class: 'text-primary cursor-pointer', onClick: recoveryTaskPromptClick }, [t('cloudbuild.clickView')])
|
||||||
|
]),
|
||||||
|
duration: 0,
|
||||||
|
showClose: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击任务提示进入窗口
|
||||||
|
const recoveryTaskPromptClick = () => {
|
||||||
|
iSBackupRecovery.value = 2
|
||||||
|
showDialog.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
notificationEl && notificationEl.close()
|
||||||
|
terminalRef.value.execute('clear')
|
||||||
|
terminalRef.value.execute('开始执行')
|
||||||
|
restoreTaskList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进入恢复窗口列表
|
||||||
|
const restoreTaskList = () => {
|
||||||
|
performRecoveryTasks({}).then(({ data }) => {
|
||||||
|
if (!data) return
|
||||||
|
cloudBuildTask.value = data
|
||||||
|
cloudBuildTask.value.data.forEach(item => {
|
||||||
|
if (!cloudBuildLog.includes(item.content)) {
|
||||||
|
terminalRef.value.pushMessage({ content: `${item.content}` })
|
||||||
|
cloudBuildLog.push(item.content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const lastTask = data.data[data.data.length - 1].task
|
||||||
|
if (lastTask === 'end' || data.data.length == 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
active.value = 'complete'
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 1500)
|
||||||
|
} else if (lastTask === 'fail') {
|
||||||
|
// 恢复失败
|
||||||
|
setTimeout(() => {
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
restoreTaskList()
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogClose = (done: () => {}) => {
|
||||||
|
if (active.value == 'execute') {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
t('showDialogCloseTips'),
|
||||||
|
t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
terminalRef.value.execute('clear')
|
||||||
|
interrupt.value = true // 执行一半,中途取消,需要恢复初始状态
|
||||||
|
done()
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (active.value == 'complete') {
|
||||||
|
// 恢复备份需要等待恢复数据加载完成,延迟刷新页面
|
||||||
|
setTimeout(() => {
|
||||||
|
location.reload()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升级进度动画
|
||||||
|
*/
|
||||||
|
let flashInterval: null | number = null
|
||||||
|
const terminalFlash = new TerminalFlash()
|
||||||
|
const onExecCmd = (key, command, success, failed, name) => {
|
||||||
|
if (command == '开始执行') {
|
||||||
|
success(terminalFlash)
|
||||||
|
const frames = makeIterator(['/', '——', '\\', '|'])
|
||||||
|
flashInterval = setInterval(() => {
|
||||||
|
terminalFlash.flush('> ' + frames.next().value)
|
||||||
|
}, 150)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeIterator = (array: string[]) => {
|
||||||
|
let nextIndex = 0
|
||||||
|
return {
|
||||||
|
next () {
|
||||||
|
if ((nextIndex + 1) == array.length) {
|
||||||
|
nextIndex = 0
|
||||||
|
}
|
||||||
|
return { value: array[nextIndex++] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => showDialog.value, () => {
|
||||||
|
if (!showDialog.value) {
|
||||||
|
active.value = 'execute'
|
||||||
|
flashInterval && clearInterval(flashInterval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const showRemarkDialog: any = ref<boolean>(false)
|
||||||
|
const remarkLoading = ref(false)
|
||||||
|
const formData: any = reactive({
|
||||||
|
id: 0,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 修改备注
|
||||||
|
const remarkEvent = (row: any) => {
|
||||||
|
formData.id = row.id
|
||||||
|
formData.remark = row.remark
|
||||||
|
showRemarkDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifyRemarkFn = () => {
|
||||||
|
remarkLoading.value = true
|
||||||
|
modifyBackupRemark({
|
||||||
|
id: formData.id,
|
||||||
|
remark: formData.remark
|
||||||
|
}).then(() => {
|
||||||
|
showRemarkDialog.value = false
|
||||||
|
remarkLoading.value = false
|
||||||
|
loadList()
|
||||||
|
}).catch(() => {
|
||||||
|
remarkLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除升级记录
|
||||||
|
const deleteEvent = (id: number) => {
|
||||||
|
ElMessageBox.confirm(t('deleteTips'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
if (repeat.value) return
|
||||||
|
repeat.value = true
|
||||||
|
tableData.loading = true
|
||||||
|
deleteRecords({
|
||||||
|
ids: id
|
||||||
|
}).then(() => {
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
repeat.value = false
|
||||||
|
tableData.loading = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除升级记录
|
||||||
|
const batchDelete = () => {
|
||||||
|
if (multipleSelection.value.length == 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: `${t('batchEmptySelectedTips')}`
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessageBox.confirm(t('deleteTips'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
tableData.loading = true
|
||||||
|
if (repeat.value) return
|
||||||
|
repeat.value = true
|
||||||
|
|
||||||
|
const ids: any = []
|
||||||
|
multipleSelection.value.forEach((item: any) => {
|
||||||
|
ids.push(item.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
deleteRecords({
|
||||||
|
ids
|
||||||
|
}).then(() => {
|
||||||
|
loadList()
|
||||||
|
repeat.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
repeat.value = false
|
||||||
|
tableData.loading = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.terminal .t-log-box span) {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-head-bg {
|
||||||
|
background-color: var(--el-table-header-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .number-of-steps {
|
||||||
|
.el-step__line {
|
||||||
|
margin: 0 25px;
|
||||||
|
background: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-step__head {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-success {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
|
||||||
|
.el-step__icon {
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
|
||||||
|
box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-step__line {
|
||||||
|
margin: 0 25px;
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-process {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-weight: inherit;
|
||||||
|
|
||||||
|
// font-size: 18px;
|
||||||
|
.el-step__icon {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid var(--el-color-primary);
|
||||||
|
box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-wait {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 多行超出隐藏 */
|
||||||
|
.multi-hidden {
|
||||||
|
word-break: break-all;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
218
admin/src/app/views/tools/cloud_compile.vue
Normal file
218
admin/src/app/views/tools/cloud_compile.vue
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading" class="main-container w-full">
|
||||||
|
<div class="p-5 bg-[#fff] overflow-hidden">
|
||||||
|
|
||||||
|
<div class="bg-[#fff] w-[100%] rounded-[8px] overflow-hidden">
|
||||||
|
<div class=" relative pb-[13px]" style="border-bottom: 2px solid #f0f2f6">
|
||||||
|
<div class="w-[66px] bg-primary h-[2px] absolute bottom-[0px]"></div>
|
||||||
|
<span class=" text-primary text-[18px] ml-[4px]">云编译</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex mt-[20px] ml-[20px]">
|
||||||
|
<el-button class="w-[98px] !h-[36px]" type="primary" @click="handleCloudBuild" :loading="cloudBuildRef?.loading">云编译</el-button>
|
||||||
|
<div class="btn-time w-[181px] h-[36px] rounded-[4px] text-[#606266] text-[14px] ml-[10px]">
|
||||||
|
<span>云编译执行时间大约</span>
|
||||||
|
<span class="text-[16px] text-[#D43030] mx-[3px]">3</span>
|
||||||
|
<span>分钟</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[21px] flex mb-[21px] items-center">
|
||||||
|
<span class="flex ml-[20px] text-[16px] items-center">
|
||||||
|
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||||
|
温馨提示
|
||||||
|
</span>
|
||||||
|
<span class="text-[14px] text-[#606266] ml-[7px]"> 以下情况可以进行云编译</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[14px] text-[#606266] ml-[13px] mb-[18px]">云编译不需要本地安装node环境即可进行,针对使用者方便快捷</div>
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">1、系统或插件,每次安装或升级完成后,需要云编译</div>
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">2、开发者编写完前端代码之后,可以使用云编译进行源码编译</div>
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">3、由于云编译不是针对某个插件进行编译,而是系统整体编译,因此如果同时需要安装多个插件时,往往需要安装到最后一个插件才整体进行云编译</div>
|
||||||
|
<div class="mt-[21px] flex mb-[21px] text-[16px] items-center">
|
||||||
|
<span class="flex ml-[20px] items-center">
|
||||||
|
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||||
|
云编译流程
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px]">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item color="#4268EF">
|
||||||
|
<template #dot>
|
||||||
|
<div class="w-[15px] h-[15px] bg-primary rounded-[50%] text-[9px] text-[#fff] flex items-center justify-center">1</div>
|
||||||
|
</template>
|
||||||
|
<div class="text-[16px] text-[#303133]">编译admin代码</div>
|
||||||
|
<div class="py-[12px] px-[10px] bg-[#F7F8FA] mt-[10px] text-[#606266] text-[14px] w-[1085px]">
|
||||||
|
<span>云编译会将admin端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud 下的</span>
|
||||||
|
<span class="text-[#FF9D31] mx-[3px]">public/admin</span>
|
||||||
|
<span>目录中。后台的访问路径将变为</span>
|
||||||
|
<span class="text-primary ml-[3px]">https://域名/admin</span>
|
||||||
|
</div>
|
||||||
|
</el-timeline-item>
|
||||||
|
<el-timeline-item color="#4268EF">
|
||||||
|
<template #dot>
|
||||||
|
<div class="w-[15px] h-[15px] bg-primary rounded-[50%] text-[9px] text-[#fff] flex items-center justify-center">2</div>
|
||||||
|
</template>
|
||||||
|
<div class="text-[16px] text-[#303133]">编译uniapp代码</div>
|
||||||
|
<div class="py-[12px] px-[10px] bg-[#F7F8FA] mt-[10px] text-[#606266] text-[14px] w-[1085px]">
|
||||||
|
<span>云编泽会将uniapp端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
||||||
|
<span class="text-[#FF9D31] mx-[3px]">public/wap</span>
|
||||||
|
<span>目录中,这样手机端网页的访问路径将变为</span>
|
||||||
|
<span class="text-primary ml-[3px]"> https://域名/wap</span>
|
||||||
|
</div>
|
||||||
|
</el-timeline-item>
|
||||||
|
<el-timeline-item color="#4268EF">
|
||||||
|
<template #dot>
|
||||||
|
<div class="w-[15px] h-[15px] bg-primary rounded-[50%] text-[9px] text-[#fff] flex items-center justify-center">3</div>
|
||||||
|
</template>
|
||||||
|
<div class="text-[16px] text-[#303133]">编译web代码</div>
|
||||||
|
<div class="py-[12px] px-[10px] bg-[#F7F8FA] mt-[10px] text-[#606266] text-[14px] w-[1085px]">
|
||||||
|
<span>云编泽会将web端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
||||||
|
<span class="text-[#FF9D31] mx-[3px]">public/web</span>
|
||||||
|
<span>目录中,这样电脑端网页的访问路径将变为</span>
|
||||||
|
<span class="text-primary ml-[3px]"> https://域名/web</span>
|
||||||
|
</div>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-5 bg-[#fff] mt-[26px]">
|
||||||
|
<div class="bg-[#fff] w-[100%] rounded-[8px] overflow-hidden">
|
||||||
|
<div class="relative pb-[13px]" style="border-bottom: 2px solid #f0f2f6">
|
||||||
|
<div class="w-[85px] bg-primary h-[2px] absolute bottom-[0px]"></div>
|
||||||
|
<span class="text-primary text-[18px] ml-[4px]">本地编译</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[21px] flex mb-[21px] text-[16px] items-center">
|
||||||
|
<span class="flex ml-[20px] items-center">
|
||||||
|
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||||
|
温馨提示
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">
|
||||||
|
<span>1、如果本地安装了Node环境,可以进行本地编译,要求</span>
|
||||||
|
<span class="text-[#D43030] ml-[3px]">Node版本>18</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">2、默认本地编译流程与云编译相同,执行本地编译命令后,会将编译后的代码移动到系统niucloud下的public下的对应端口目录下</div>
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">3、由于云编译配置的访问路径时固定的,针对客户有独立部署admin,wap,web等个性化端口名称配置需求,需要进行本地编译</div>
|
||||||
|
<div class="mt-[34px] flex mb-[21px] text-[16px] items-center">
|
||||||
|
<span class="flex ml-[20px] items-center">
|
||||||
|
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||||
|
本地编译命令参考
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||||
|
<span class=" text-[#303133]">安装依赖:</span>
|
||||||
|
进入admin端与uniapp端以及web端目录都可执行
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||||
|
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm install</span>
|
||||||
|
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm install')">复制命令</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[21px]">
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||||
|
<span class="text-[#303133]">后台admin端口打包:</span>
|
||||||
|
<span>进入admin目录下执行,执行后编译代码默认移动到系统的niucloud下的</span>
|
||||||
|
<span class="text-[#FF9D31] mx-[3px]">public/admin</span>
|
||||||
|
<span>目录下</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||||
|
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run build</span>
|
||||||
|
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build')">复制命令</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[21px]">
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||||
|
<span class="text-[#303133]">使用uniapp打包H5:</span>
|
||||||
|
<span>进入uniapp目录下执行,执行后编译代码默认移动到系统niucloud下的</span>
|
||||||
|
<span class="text-[#FF9D31] mx-[3px]">public/wap</span>
|
||||||
|
<span>目录下</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||||
|
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run build:h5</span>
|
||||||
|
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build:h5')">复制命令</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[21px]">
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||||
|
<span class=" text-[#303133]">使用uniapp打包微信小程序:</span>
|
||||||
|
<span>进入uniapp目录下执行,执行后编译代码默认移动到系统niucloud下的</span>
|
||||||
|
<span class="text-[#FF9D31] mx-[3px]">uni-app/dist/build/mp-weixin</span>
|
||||||
|
<span>目录</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||||
|
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run build:mp-weixin</span>
|
||||||
|
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build:mp-weixin')">复制命令</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[21px]">
|
||||||
|
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||||
|
<span class="text-[#303133]">web端打包:</span>
|
||||||
|
<span>进入web目录下执行,执行后编译代码默认移动到系统niucloud下的</span>
|
||||||
|
<span class="text-[#FF9D31] mx-[3px]">public/web</span>
|
||||||
|
<span>目录下</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||||
|
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run generate</span>
|
||||||
|
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build')">复制命令</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<upgrade ref="upgradeRef" @cloudbuild="handleCloudBuild"/>
|
||||||
|
<cloud-build ref="cloudBuildRef"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch } from "vue"
|
||||||
|
import { ElMessage } from "element-plus"
|
||||||
|
import { useClipboard } from "@vueuse/core"
|
||||||
|
import { t } from "@/lang"
|
||||||
|
import Upgrade from "@/app/components/upgrade/index.vue"
|
||||||
|
import CloudBuild from "@/app/components/cloud-build/index.vue"
|
||||||
|
|
||||||
|
const loading = ref<Boolean>(false)
|
||||||
|
|
||||||
|
// 云编译调用
|
||||||
|
const cloudBuildRef = ref<any>(null)
|
||||||
|
const handleCloudBuild = () => {
|
||||||
|
ElMessageBox.confirm(t("cloudBuildTips"), t("warning"), {
|
||||||
|
confirmButtonText: t("confirm"),
|
||||||
|
cancelButtonText: t("cancel"),
|
||||||
|
type: "warning"
|
||||||
|
}).then(() => {
|
||||||
|
cloudBuildRef.value?.open()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制命令
|
||||||
|
const { copy, isSupported, copied } = useClipboard()
|
||||||
|
|
||||||
|
const copyEvent = (text: string) => {
|
||||||
|
if (!isSupported.value) {
|
||||||
|
ElMessage({
|
||||||
|
message: t("notSupportCopy"),
|
||||||
|
type: "warning"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(copied, () => {
|
||||||
|
if (copied.value) {
|
||||||
|
ElMessage({
|
||||||
|
message: t("copySuccess"),
|
||||||
|
type: "success"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.btn-time {
|
||||||
|
line-height: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -203,8 +203,7 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
|||||||
const setFormData = async(row: any = null) => {
|
const setFormData = async(row: any = null) => {
|
||||||
formData.value = cloneDeep(Object.assign(initialFormData, row))
|
formData.value = cloneDeep(Object.assign(initialFormData, row))
|
||||||
getDictAllFn()
|
getDictAllFn()
|
||||||
if(formData.value.model != '')
|
if (formData.value.model != '') {
|
||||||
{
|
|
||||||
getGeneratorAllModelFn({ addon: formData.value.addon })
|
getGeneratorAllModelFn({ addon: formData.value.addon })
|
||||||
getGeneratorModelTableColumnFn({ model: formData.value.model })
|
getGeneratorModelTableColumnFn({ model: formData.value.model })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -174,7 +174,7 @@
|
|||||||
import { reactive, ref, onMounted } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getGenerateTableList, deleteGenerateTable, generateCreate, generatePreview, generatorCheckFile, getAddonDevelop } from '@/app/api/tools'
|
import { getGenerateTableList, deleteGenerateTable, generateCreate, generatePreview, generatorCheckFile, getAddonDevelop } from '@/app/api/tools'
|
||||||
import { img } from '@/utils/common'
|
import { img,setTablePageStorage,getTablePageStorage } from '@/utils/common'
|
||||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||||
import AddTable from '@/app/views/tools/code/components/add-table.vue'
|
import AddTable from '@/app/views/tools/code/components/add-table.vue'
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
@ -211,7 +211,7 @@ onMounted(() => {
|
|||||||
activeName.value = window.codeActiveName + ''
|
activeName.value = window.codeActiveName + ''
|
||||||
window.codeActiveName = null
|
window.codeActiveName = null
|
||||||
}
|
}
|
||||||
loadGenerateTableList()
|
loadGenerateTableList(getTablePageStorage(codeTableData.searchParam).page)
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* 获取代码生成列表
|
* 获取代码生成列表
|
||||||
@ -228,6 +228,7 @@ const loadGenerateTableList = (page: number = 1) => {
|
|||||||
codeTableData.loading = false
|
codeTableData.loading = false
|
||||||
codeTableData.data = res.data.data
|
codeTableData.data = res.data.data
|
||||||
codeTableData.total = res.data.total
|
codeTableData.total = res.data.total
|
||||||
|
setTablePageStorage(codeTableData.page, codeTableData.limit, codeTableData.searchParam);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
codeTableData.loading = false
|
codeTableData.loading = false
|
||||||
})
|
})
|
||||||
|
|||||||
@ -75,10 +75,6 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getSystem } from '@/app/api/tools'
|
import { getSystem } from '@/app/api/tools'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const pageName = route.meta.title
|
|
||||||
|
|
||||||
const systemService = ref({})
|
const systemService = ref({})
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main-container h-[500px] w-full p-5 bg-white" v-loading="loading">
|
<div class="main-container" >
|
||||||
<div class="flex flex-wrap px-2 plug-list pb-10">
|
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
|
||||||
<div class="flex items-center bg-[#F7F8FA] p-3 w-[295px] relative plug-item mr-4 mb-4 cursor-pointer">
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-page-title">{{ pageName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap px-2 plug-list pb-10 mt-[20px] ">
|
||||||
|
<div class="flex items-center p-3 w-[295px] relative plug-item mr-4 mb-4 bg-[var(--el-color-info-light-9)] cursor-pointer">
|
||||||
<div class="flex flex-col ml-2">
|
<div class="flex flex-col ml-2">
|
||||||
<span class="text-sm truncate w-[190px]">{{t('dataCache')}}</span>
|
<span class="text-sm truncate w-[190px]">{{t('dataCache')}}</span>
|
||||||
<span class="text-xs text-gray-400 mt-1 truncate w-[190px]" :title="t('dataCacheDesc')">{{t('dataCacheDesc')}}</span>
|
<span class="text-xs text-gray-400 mt-1 truncate w-[190px]" :title="t('dataCacheDesc')">{{t('dataCacheDesc')}}</span>
|
||||||
@ -9,6 +13,7 @@
|
|||||||
<span class="plug-item-operate" @click="schemaCache()">{{t('refresh')}}</span>
|
<span class="plug-item-operate" @click="schemaCache()">{{t('refresh')}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -17,8 +22,10 @@ import { ref } from 'vue'
|
|||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { clearCache } from '@/app/api/sys'
|
import { clearCache } from '@/app/api/sys'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
const loading = ref<Boolean>(false)
|
const loading = ref<Boolean>(false)
|
||||||
|
const route = useRoute()
|
||||||
|
const pageName = route.meta.title
|
||||||
|
|
||||||
// 数据缓存
|
// 数据缓存
|
||||||
const schemaCache = () => {
|
const schemaCache = () => {
|
||||||
|
|||||||
161
admin/src/app/views/tools/upgrade_records.vue
Normal file
161
admin/src/app/views/tools/upgrade_records.vue
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="main-container">
|
||||||
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-page-title">{{ pageName }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||||
|
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
|
||||||
|
<el-form-item :label="t('upgradeName')" prop="name">
|
||||||
|
<el-input v-model.trim="tableData.searchParam.name" :placeholder="t('upgradeNamePlaceholder')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="loadList()">{{ t('search') }}</el-button>
|
||||||
|
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<div class="mb-[10px] flex items-center">
|
||||||
|
<el-button @click="batchDelete" size="small">{{ t('batchDelete') }}</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="tableData.data" size="large" v-loading="tableData.loading" ref="tableRef" @selection-change="handleSelectionChange">
|
||||||
|
|
||||||
|
<template #empty>
|
||||||
|
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="id" :label="t('id')" width="140" />
|
||||||
|
<el-table-column prop="name" :label="t('upgradeName')" >
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="!row.content || typeof row.content == 'string'">
|
||||||
|
【{{ row.name }}】从{{ row.prev_version }}升级到{{ row.current_version }}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div v-for="item in row.content.content">【{{ item.app.app_name }}】从{{ item.version }}升级到{{ item.upgrade_version }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="create_time" :label="t('completeTime')" width="220px" />
|
||||||
|
<el-table-column prop="status_name" :label="t('status')" width="120px" />
|
||||||
|
<el-table-column :label="t('operation')" align="right" width="160px">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link v-if="row.status == 'fail'" @click="handleFailReason(row)">{{ t('failReason') }}</el-button>
|
||||||
|
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="mt-[16px] flex justify-end">
|
||||||
|
<el-pagination v-model:current-page="tableData.page"
|
||||||
|
v-model:page-size="tableData.limit" layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="tableData.total" @size-change="loadList()"
|
||||||
|
@current-change="loadList" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import {ElMessage, ElMessageBox, FormInstance} from 'element-plus'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { getUpgradeRecords, delUpgradeRecords } from '@/app/api/upgrade'
|
||||||
|
import 'vue-web-terminal/lib/theme/dark.css'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const pageName = route.meta.title
|
||||||
|
const searchFormRef = ref<FormInstance>()
|
||||||
|
const tableRef = ref()
|
||||||
|
const tableData: any = reactive({
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: true,
|
||||||
|
data: [],
|
||||||
|
searchParam: {
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const resetForm = (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
formEl.resetFields()
|
||||||
|
loadList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取列表
|
||||||
|
*/
|
||||||
|
const loadList = (page: number = 1) => {
|
||||||
|
tableData.loading = true
|
||||||
|
tableData.page = page
|
||||||
|
getUpgradeRecords({
|
||||||
|
page: tableData.page,
|
||||||
|
limit: tableData.limit,
|
||||||
|
...tableData.searchParam
|
||||||
|
}).then(res => {
|
||||||
|
tableData.loading = false
|
||||||
|
tableData.data = res.data.data
|
||||||
|
tableData.total = res.data.total
|
||||||
|
}).catch(() => {
|
||||||
|
tableData.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadList()
|
||||||
|
|
||||||
|
const failReason = ref('')
|
||||||
|
const failReasonDialogVisible = ref(false)
|
||||||
|
const handleFailReason = (data: any) => {
|
||||||
|
failReason.value = data.fail_reason
|
||||||
|
failReasonDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let ids = []
|
||||||
|
|
||||||
|
const handleSelectionChange = (e: any) => {
|
||||||
|
ids = e.map(item => item.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const batchDelete = () => {
|
||||||
|
if (!ids.length) {
|
||||||
|
ElMessage({ message: '请先勾选要删除的记录', type: 'error', duration: 5000 })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
deleteEvent(ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteEvent = (ids: any) => {
|
||||||
|
ElMessageBox.confirm(t('deleteTips'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
delUpgradeRecords({
|
||||||
|
ids: ids
|
||||||
|
}).then(() => {
|
||||||
|
loadList()
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
||||||
@ -7,7 +7,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { getToken, img } from '@/utils/common'
|
import { getToken, img } from '@/utils/common'
|
||||||
import { VueUeditorWrap } from 'vue-ueditor-wrap'
|
import { VueUeditorWrap } from 'vue-ueditor-wrap'
|
||||||
import storage from '@/utils/storage'
|
import storage from '@/utils/storage'
|
||||||
@ -84,7 +84,11 @@ const handleEditorReady = (editor) => {
|
|||||||
// // 同步到统计栏(需操作DOM)
|
// // 同步到统计栏(需操作DOM)
|
||||||
// updateStatsDisplay(charCount.value)
|
// updateStatsDisplay(charCount.value)
|
||||||
// })
|
// })
|
||||||
console.log('扩展原型链', editor)
|
// console.log('扩展原型链', editor)
|
||||||
|
editor.addListener('blur', () => {
|
||||||
|
// console.log('失焦了')
|
||||||
|
emit('handleBlur', editor.getContent()) // 把内容传出去
|
||||||
|
})
|
||||||
|
|
||||||
// 方案二:原型链扩展(如果编辑器版本支持)
|
// 方案二:原型链扩展(如果编辑器版本支持)
|
||||||
const originalCount = editor.getContentLength; // 原生统计方法
|
const originalCount = editor.getContentLength; // 原生统计方法
|
||||||
|
|||||||
87
admin/src/components/markdown/index.vue
Normal file
87
admin/src/components/markdown/index.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div id="vditor" />
|
||||||
|
<upload-attachment type="image" ref="imageRef" limit="" @confirm="imageSelect" />
|
||||||
|
<upload-attachment type="video" ref="videoRef" @confirm="videoSelect" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import Vditor from 'vditor'
|
||||||
|
import 'vditor/dist/index.css'
|
||||||
|
import { img } from '@/utils/common'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 600
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'wysiwyg'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:modelValue', 'handleBlur'])
|
||||||
|
|
||||||
|
const content = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emits('update:modelValue', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const imageRef: Record<string, any> | null = ref(null)
|
||||||
|
const videoRef: Record<string, any> | null = ref(null)
|
||||||
|
const vditor = ref<Vditor | null>(null)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
vditor.value = new Vditor('vditor', {
|
||||||
|
height: props.height,
|
||||||
|
after: () => {
|
||||||
|
vditor.value!.setValue(content.value)
|
||||||
|
},
|
||||||
|
mode: props.mode,
|
||||||
|
toolbar: [
|
||||||
|
'emoji', 'headings', 'bold', 'italic', 'strike', '|', 'line', 'quote', 'list', 'ordered-list', 'check', 'outdent', 'indent',
|
||||||
|
{
|
||||||
|
name: 'image',
|
||||||
|
tip: '插入图片',
|
||||||
|
icon: '<svg t="1743135560768" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="32640" width="200" height="200"><path d="M734.608696 155.826087a200.347826 200.347826 0 0 1 200.347826 200.347826v311.652174c0 32.122435-7.568696 62.464-20.992 89.35513L410.000696 461.401043a100.173913 100.173913 0 0 0-111.549218 6.811827L89.043478 628.357565V356.173913a200.347826 200.347826 0 0 1 200.347826-200.347826h445.217392zM376.208696 518.989913L874.362435 811.408696A199.68 199.68 0 0 1 734.608696 868.173913H289.391304c-96.478609 0-177.040696-68.185043-196.073739-159.009391l245.693218-187.881739a33.391304 33.391304 0 0 1 37.175652-2.29287zM289.391304 89.043478C141.868522 89.043478 22.26087 208.65113 22.26087 356.173913v311.652174c0 147.522783 119.607652 267.130435 267.130434 267.130435h445.217392c147.522783 0 267.130435-119.607652 267.130434-267.130435V356.173913c0-147.522783-119.607652-267.130435-267.130434-267.130435H289.391304z m445.217392 356.173913a89.043478 89.043478 0 1 0 0-178.086956 89.043478 89.043478 0 0 0 0 178.086956z" fill="#333333" p-id="32641"></path></svg>',
|
||||||
|
click: () => {
|
||||||
|
imageRef.value.showDialog = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'video',
|
||||||
|
tip: '插入视频',
|
||||||
|
icon: '<svg t="1743136124938" class="icon" viewBox="0 0 1256 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="34055" width="200" height="200"><path d="M201.681455 74.472727a111.709091 111.709091 0 0 0-111.709091 111.709091v651.636364a111.709091 111.709091 0 0 0 111.709091 111.709091h837.818181a111.709091 111.709091 0 0 0 111.709091-111.709091V186.181818a111.709091 111.709091 0 0 0-111.709091-111.709091h-837.818181z m0-74.472727h837.818181a186.181818 186.181818 0 0 1 186.181819 186.181818v651.636364a186.181818 186.181818 0 0 1-186.181819 186.181818h-837.818181a186.181818 186.181818 0 0 1-186.181819-186.181818V186.181818a186.181818 186.181818 0 0 1 186.181819-186.181818zM798.72 533.085091a9.309091 9.309091 0 0 0-1.861818-13.032727l-248.226909-186.181819a9.309091 9.309091 0 0 0-14.894546 7.447273v372.363637a9.309091 9.309091 0 0 0 14.894546 7.447272l248.226909-186.181818a9.309091 9.309091 0 0 0 1.861818-1.861818z m-205.405091 247.621818a83.781818 83.781818 0 0 1-134.050909-67.025454v-372.363637a83.781818 83.781818 0 0 1 134.050909-67.025454l248.226909 186.181818a83.781818 83.781818 0 0 1 0 134.050909l-248.226909 186.181818z" fill="#000000" p-id="34056"></path></svg>',
|
||||||
|
click: () => {
|
||||||
|
videoRef.value.showDialog = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'code', 'inline-code', 'insert-after', 'insert-before ', 'undo', 'redo', 'link', 'table', 'record', 'edit-mode', 'both', 'preview', 'fullscreen', 'outline', 'code-theme', 'content-theme', 'export', 'br'
|
||||||
|
],
|
||||||
|
blur: () => {
|
||||||
|
content.value = vditor.value!.getValue()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const imageSelect = (data: Record<string, any>) => {
|
||||||
|
data.forEach((item: any) => {
|
||||||
|
vditor.value.insertValue(`})`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoSelect = (data: Record<string, any>) => {
|
||||||
|
vditor.value.insertValue(`<video src="${img(data.url)}">`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, ref, watch} from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
|||||||
158
admin/src/components/spread-popup/index.vue
Normal file
158
admin/src/components/spread-popup/index.vue
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="showDialog" :title="titleName" width="500px" :destroy-on-close="true">
|
||||||
|
<el-tabs v-model="channel" class="mb-[10px]">
|
||||||
|
<el-tab-pane label="H5" name="h5"></el-tab-pane>
|
||||||
|
<el-tab-pane label="微信小程序" name="weapp"></el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<!-- H5推广 -->
|
||||||
|
<div class="promote-flex flex" v-if="channel === 'h5'">
|
||||||
|
<div class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]">
|
||||||
|
<el-image :src="wapImage"/>
|
||||||
|
</div>
|
||||||
|
<div class="px-[20px] flex-1">
|
||||||
|
<div class="mb-[10px]">{{ t('promoteUrl') }}</div>
|
||||||
|
<el-input class="mb-[10px]" readonly :value="wapPreview">
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="copyEvent(wapPreview)">{{ t('copy') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<a class="text-primary" :href="wapImage" download>{{ t('downLoadQRCode') }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 小程序推广 -->
|
||||||
|
<div class="promote-flex flex" v-if="channel === 'weapp'">
|
||||||
|
<div class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]">
|
||||||
|
<el-image :src="img(weappData.path)" v-if="weappData.path" class="w-[150px] h-[150px]">
|
||||||
|
<template #error>
|
||||||
|
<div class="w-[150px] h-[150px] text-[14px] text-center leading-[150px]">{{ t('configureFailed') }}</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<div v-else class="w-[150px] h-[150px] text-[14px] text-center leading-[150px]">{{ t('configureFailed') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-[20px] flex-1" v-if="weappData.path">
|
||||||
|
<a class="text-primary" :href="img(weappData.path)" target="_blank" download>{{ t('downLoadQRCode') }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref, reactive, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
import { getUrl, getQrcode } from '@/app/api/sys'
|
||||||
|
import { img } from '@/utils/common'
|
||||||
|
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const channel = ref('h5')
|
||||||
|
const wapUrl = ref('')
|
||||||
|
const wapDomain = ref('')
|
||||||
|
const wapImage = ref('')
|
||||||
|
const wapPreview = ref('')
|
||||||
|
const pageName = ref('')
|
||||||
|
const weappData = reactive({
|
||||||
|
path: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
getUrl().then((res: any) => {
|
||||||
|
wapUrl.value = res.data.wap_url
|
||||||
|
|
||||||
|
// 生产模式禁止
|
||||||
|
if (import.meta.env.MODE == 'production') return
|
||||||
|
|
||||||
|
wapDomain.value = res.data.wap_domain
|
||||||
|
|
||||||
|
// env文件配置过wap域名
|
||||||
|
if (wapDomain.value) {
|
||||||
|
wapUrl.value = wapDomain.value + '/wap'
|
||||||
|
}
|
||||||
|
|
||||||
|
const wapDomainStorage = storage.get('wap_domain')
|
||||||
|
if (wapDomainStorage) {
|
||||||
|
wapUrl.value = wapDomainStorage
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// **生成H5二维码**
|
||||||
|
const generateH5QRCode = () => {
|
||||||
|
wapPreview.value = `${ wapUrl.value }${ pageName.value }`
|
||||||
|
QRCode.toDataURL(wapPreview.value, { errorCorrectionLevel: 'L', margin: 0, width: 120 }).then(url => {
|
||||||
|
wapImage.value = url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// **获取小程序二维码**
|
||||||
|
const fetchWeAppQRCode = () => {
|
||||||
|
// 去掉 page 参数前面的 '/'
|
||||||
|
if (pagePath.value.startsWith('/')) {
|
||||||
|
pagePath.value = pagePath.value.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getQrcode({
|
||||||
|
page: pagePath.value, // 传递页面路径
|
||||||
|
folder: folder.value, // 传递模块目录
|
||||||
|
params: [
|
||||||
|
{
|
||||||
|
column_name: columnName.value,
|
||||||
|
column_value: columnValue.value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
weappData.path = res.data.weapp_path
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义变量存储传入的数据
|
||||||
|
const pagePath = ref("")
|
||||||
|
const columnName = ref("")
|
||||||
|
const columnValue = ref("")
|
||||||
|
const titleName = ref("")
|
||||||
|
const folder: any = ref("")
|
||||||
|
|
||||||
|
// **显示对话框**
|
||||||
|
const show = (page: string, column: string, value: string, title: string, dir: string) => {
|
||||||
|
pagePath.value = page
|
||||||
|
columnName.value = column
|
||||||
|
columnValue.value = value
|
||||||
|
titleName.value = title
|
||||||
|
folder.value = dir
|
||||||
|
pageName.value = `${ pagePath.value }?${ columnName.value }=${ columnValue.value }`
|
||||||
|
|
||||||
|
generateH5QRCode()
|
||||||
|
fetchWeAppQRCode()
|
||||||
|
showDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// **复制链接**
|
||||||
|
const { copy, isSupported, copied } = useClipboard()
|
||||||
|
const copyEvent = (text: string) => {
|
||||||
|
if (!isSupported.value) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('notSupportCopy'),
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
copy(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(copied, () => {
|
||||||
|
if (copied.value) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('copySuccess'),
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// **暴露给父组件**
|
||||||
|
defineExpose({
|
||||||
|
show
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -28,7 +28,8 @@
|
|||||||
<icon name="iconfont icon24gf-playCircle" color="#fff" size="25px" @click="previewVideo(index)" />
|
<icon name="iconfont icon24gf-playCircle" color="#fff" size="25px" @click="previewVideo(index)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<icon name="element CircleCloseFilled" color="#bbb" size="18px" @click="removeVideo(index)" class="absolute z-[2] top-[-9px] right-[-9px]"/>
|
<icon name="element CircleCloseFilled" color="#bbb" size="18px" @click="removeVideo(index)"
|
||||||
|
class="absolute z-[2] top-[-9px] right-[-9px]" />
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded cursor-pointer relative bg-page video-wrap mr-[10px]" :style="style" v-if="videos.data.length < limit">
|
<div class="rounded cursor-pointer relative bg-page video-wrap mr-[10px]" :style="style" v-if="videos.data.length < limit">
|
||||||
<upload-attachment :limit="limit" type="video" @confirm="confirmSelect">
|
<upload-attachment :limit="limit" type="video" @confirm="confirmSelect">
|
||||||
|
|||||||
@ -162,12 +162,17 @@
|
|||||||
"readable": "可读",
|
"readable": "可读",
|
||||||
"write": "可写",
|
"write": "可写",
|
||||||
"upgradeSuccess": "升级成功",
|
"upgradeSuccess": "升级成功",
|
||||||
"localBuild": "手动编译",
|
"localBuild": "本地编译",
|
||||||
"cloudBuild": "云编译",
|
"cloudBuild": "重试",
|
||||||
|
"rollback": "回滚",
|
||||||
"showDialogCloseTips": "升级任务尚未完成,关闭将取消升级,是否要继续关闭?",
|
"showDialogCloseTips": "升级任务尚未完成,关闭将取消升级,是否要继续关闭?",
|
||||||
"upgradeCompleteTips": "升级完成后还需要编译admin wap web端可选择云编译或者是手动编译",
|
"upgradeCompleteTips": "升级完成后还必须要重新编译admin wap web端,以免影响到程序正常运行。",
|
||||||
"upgradeTips": "应用和插件升级时,系统会自动备份当前程序及数据库。升级功能不会造成您当前程序的损坏或者数据的丢失,请放心使用,但是升级过程可能会因为兼容性等各种原因出现意外的升级错误,当出现错误时,请参考链接<a href='https://www.kancloud.cn/niushop/niushop_v6/3228611' target='_blank' class='text-primary'> https://www.kancloud.cn/niushop/niushop_v6/3228611 </a>手动回退上一版本!",
|
"upgradeTips": "应用和插件升级时,系统会自动备份当前程序及数据库。升级功能不会造成您当前程序的损坏或者数据的丢失,请放心使用,但是升级过程可能会因为兼容性等各种原因出现意外的升级错误,当出现错误时,请参考链接<a href='https://www.kancloud.cn/niushop/niushop_v6/3228611' target='_blank' class='text-primary'> https://www.kancloud.cn/niushop/niushop_v6/3228611 </a>手动回退上一版本!",
|
||||||
"knownToKnow": "我已知晓,不需要再次提示"
|
"knownToKnow": "我已知晓,不需要再次提示",
|
||||||
|
"cloudBuildErrorTips": "一键云编译队列任务过多,请等待几分钟后重试!",
|
||||||
|
"isNeedBackup": "是否需要备份",
|
||||||
|
"isNeedBackupTips": "检测到已存在备份,此次升级是否需要备份,不需要备份在升级出现异常时将会恢复最近的一次备份。",
|
||||||
|
"isNeedBackupBtn": "查看备份记录"
|
||||||
},
|
},
|
||||||
"cloudbuild": {
|
"cloudbuild": {
|
||||||
"title": "云编译",
|
"title": "云编译",
|
||||||
@ -186,6 +191,30 @@
|
|||||||
"formSelectContentTypeName": "表单类型",
|
"formSelectContentTypeName": "表单类型",
|
||||||
"formSelectContentTypeNamePlaceholder": "请选择表单类型",
|
"formSelectContentTypeNamePlaceholder": "请选择表单类型",
|
||||||
"formSelectContentTypeAll": "全部",
|
"formSelectContentTypeAll": "全部",
|
||||||
"formSelectContentTips": "请选择表单"
|
"formSelectContentTips": "请选择表单",
|
||||||
|
"appName": "应用名/版本信息",
|
||||||
|
"appIdentification": "应用标识",
|
||||||
|
"introduction": "简介",
|
||||||
|
"type": "类型",
|
||||||
|
"localAppText": "插件管理",
|
||||||
|
"upgrade2": "升级",
|
||||||
|
"installLabel": "已安装",
|
||||||
|
"uninstalledLabel": "未安装",
|
||||||
|
"buyLabel": "已购买",
|
||||||
|
"cloudBuild": "云编译",
|
||||||
|
"newVersion": "最新版本",
|
||||||
|
"tipText": "标识指开发应用或插件的文件夹名称",
|
||||||
|
"gxx": "更新信息",
|
||||||
|
"return": "返回",
|
||||||
|
"nextStep": "下一步",
|
||||||
|
"prev": "上一步",
|
||||||
|
"viewUpgradeContent": "查看升级内容",
|
||||||
|
"testDirectoryPermissions": "检测目录权限",
|
||||||
|
"backupFiles": "备份文件",
|
||||||
|
"startUpgrade": "开始升级",
|
||||||
|
"upgradeEnd": "升级完成",
|
||||||
|
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
|
||||||
|
"promoteUrl": "推广链接",
|
||||||
|
"downLoadQRCode": "下载二维码",
|
||||||
|
"configureFailed": "配置失败"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,15 +17,10 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
||||||
<template #title>
|
<template #title>
|
||||||
<el-tooltip placement="right" effect="light">
|
|
||||||
<template #content>
|
|
||||||
该功能仅限{{ addons[meta.addon].title }}使用
|
|
||||||
</template>
|
|
||||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
</div>
|
</div>
|
||||||
<span class="ml-[10px]">{{ meta.title }}</span>
|
<span class="ml-[10px]">{{ meta.title }}</span>
|
||||||
</el-tooltip>
|
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
||||||
@ -45,14 +40,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { img } from '@/utils/common'
|
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import useSystemStore from '@/stores/modules/system'
|
import useSystemStore from '@/stores/modules/system'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const userStore = useUserStore()
|
|
||||||
const routers = useUserStore().routers
|
const routers = useUserStore().routers
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
routes: {
|
routes: {
|
||||||
|
|||||||
@ -25,9 +25,9 @@
|
|||||||
<!-- 预览 只有站点时展示-->
|
<!-- 预览 只有站点时展示-->
|
||||||
<i class="iconfont iconicon_huojian1 cursor-pointer px-[8px]" :title="t('visitWap')" @click="toPreview"></i>
|
<i class="iconfont iconicon_huojian1 cursor-pointer px-[8px]" :title="t('visitWap')" @click="toPreview"></i>
|
||||||
<!-- 切换语言 -->
|
<!-- 切换语言 -->
|
||||||
<div class="navbar-item flex items-center h-full cursor-pointer">
|
<!-- <div class="navbar-item flex items-center h-full cursor-pointer">-->
|
||||||
<switch-lang />
|
<!-- <switch-lang />-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
<!-- 切换全屏 -->
|
<!-- 切换全屏 -->
|
||||||
<!-- <div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
|
<!-- <div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
|
||||||
<icon name="iconfont icontuichuquanping" v-if="isFullscreen" />
|
<icon name="iconfont icontuichuquanping" v-if="isFullscreen" />
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import useSystemStore from '@/stores/modules/system'
|
import useSystemStore from '@/stores/modules/system'
|
||||||
import { useDark, useToggle } from '@vueuse/core'
|
import { useDark, useToggle } from '@vueuse/core'
|
||||||
import { setThemeColor, img } from '@/utils/common'
|
import { setThemeColor } from '@/utils/common'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import Storage from '@/utils/storage'
|
import Storage from '@/utils/storage'
|
||||||
|
|
||||||
|
|||||||
@ -134,7 +134,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 修改密码 --- end
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-aside :class="['h-screen layout-aside w-auto', { 'bright': !dark }]">
|
<el-aside :class="['h-screen layout-aside w-auto h-screen', { 'bright': !dark }]">
|
||||||
<side class="hidden-xs-only" />
|
<side class="hidden-xs-only" />
|
||||||
</el-aside>
|
</el-aside>
|
||||||
|
|
||||||
@ -13,14 +13,15 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, computed } from 'vue'
|
import { watch, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import side from './side.vue'
|
|
||||||
import useSystemStore from '@/stores/modules/system'
|
import useSystemStore from '@/stores/modules/system'
|
||||||
|
import side from './side.vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
const systemStore = useSystemStore()
|
const systemStore = useSystemStore()
|
||||||
const dark = computed(() => {
|
const dark = computed(() => {
|
||||||
return systemStore.dark
|
return systemStore.dark
|
||||||
})
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
systemStore.$patch(state => {
|
systemStore.$patch(state => {
|
||||||
state.menuDrawer = false
|
state.menuDrawer = false
|
||||||
@ -34,10 +35,8 @@ watch(route, () => {
|
|||||||
border-right: 1px solid var(--el-border-color-lighter);
|
border-right: 1px solid var(--el-border-color-lighter);
|
||||||
|
|
||||||
&.bright {
|
&.bright {
|
||||||
// background-color: #F5F7F9;
|
|
||||||
|
|
||||||
li {
|
li {
|
||||||
// background-color: #F5F7F9;
|
|
||||||
|
|
||||||
&.is-active:not(.is-opened) {
|
&.is-active:not(.is-opened) {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -9,12 +9,7 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
||||||
<template #title>
|
<template #title>
|
||||||
<el-tooltip placement="right" effect="light">
|
|
||||||
<template #content>
|
|
||||||
该功能仅限{{ addons[meta.addon].title }}使用
|
|
||||||
</template>
|
|
||||||
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
||||||
</el-tooltip>
|
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
||||||
@ -31,9 +26,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { img } from '@/utils/common'
|
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import useUserStore from '@/stores/modules/user'
|
|
||||||
import useSystemStore from "@/stores/modules/system";
|
import useSystemStore from "@/stores/modules/system";
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -60,6 +53,7 @@ const addons = computed(() => {
|
|||||||
.el-icon {
|
.el-icon {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-header>
|
</el-header>
|
||||||
<el-scrollbar class="h-[calc( 100vh - 64px )]">
|
<el-scrollbar class="h-[calc( 100vh - 64px )]">
|
||||||
<el-menu :default-active="oneMenuActive" :router="true" class="aside-menu" unique-opened="true" :collapse="systemStore.menuIsCollapse">
|
<el-menu :default-active="oneMenuActive" :router="true" class="aside-menu" :unique-opened="true" :collapse="systemStore.menuIsCollapse">
|
||||||
<template v-for="(item, index) in oneMenuData" :key="index">
|
<template v-for="(item, index) in oneMenuData" :key="index">
|
||||||
<el-menu-item :index="item.original_name" @click="router.push({ name: item.name })" v-if="item.meta.show">
|
<el-menu-item :index="item.original_name" @click="router.push({ name: item.name })" v-if="item.meta.show">
|
||||||
<div v-if="item.meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
<div v-if="item.meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
||||||
@ -33,8 +33,11 @@
|
|||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<el-scrollbar v-if="twoMenuData.length" class="two-menu w-[140px]">
|
<el-scrollbar v-if="twoMenuData.length" class="two-menu w-[140px]">
|
||||||
<div class="w-[140px] h-[64px] flex items-center justify-center text-[16px] border-0 border-b-[1px] border-solid border-[#eee]">{{ route.matched[1].meta.title }}</div>
|
<div class="w-[140px] h-[64px] flex items-center justify-center text-[16px] border-b-[1px] border-solid border-[var(--el-border-color-lighter)]">
|
||||||
<el-menu :default-active="route.name" :router="true" class="aside-menu" :collapse="systemStore.menuIsCollapse">
|
{{ route.matched[1].meta.title }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-menu class="aside-menu" :default-active="route.name" :default-openeds="menuOption" :router="true" :collapse="systemStore.menuIsCollapse">
|
||||||
<menu-item v-for="(route, index) in twoMenuData" :routes="route" :key="index" />
|
<menu-item v-for="(route, index) in twoMenuData" :routes="route" :key="index" />
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<div class="h-[48px]"></div>
|
<div class="h-[48px]"></div>
|
||||||
@ -51,6 +54,7 @@ import useUserStore from '@/stores/modules/user'
|
|||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import { img, isUrl } from '@/utils/common'
|
import { img, isUrl } from '@/utils/common'
|
||||||
import { findFirstValidRoute } from '@/router/routers'
|
import { findFirstValidRoute } from '@/router/routers'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
const systemStore = useSystemStore()
|
const systemStore = useSystemStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@ -66,10 +70,12 @@ const addonRouters: Record<string, any> = {}
|
|||||||
routers.forEach(item => {
|
routers.forEach(item => {
|
||||||
item.original_name = item.name
|
item.original_name = item.name
|
||||||
if (item.meta.addon == '') {
|
if (item.meta.addon == '') {
|
||||||
|
if (item.meta.attr == '') {
|
||||||
if (item.children && item.children.length) {
|
if (item.children && item.children.length) {
|
||||||
item.name = findFirstValidRoute(item.children)
|
item.name = findFirstValidRoute(item.children)
|
||||||
}
|
}
|
||||||
oneMenuData.value.push(item)
|
oneMenuData.value.push(item)
|
||||||
|
}
|
||||||
} else if (item.meta.addon != '' && systemStore?.apps.length == 1 && systemStore?.apps[0].key == item.meta.addon) {
|
} else if (item.meta.addon != '' && systemStore?.apps.length == 1 && systemStore?.apps[0].key == item.meta.addon) {
|
||||||
if (item.children) {
|
if (item.children) {
|
||||||
item.children.forEach((citem: Record<string, any>) => {
|
item.children.forEach((citem: Record<string, any>) => {
|
||||||
@ -91,18 +97,10 @@ routers.forEach(item => {
|
|||||||
if (systemStore?.apps.length > 1) {
|
if (systemStore?.apps.length > 1) {
|
||||||
const routers:Record<string, any>[] = []
|
const routers:Record<string, any>[] = []
|
||||||
systemStore?.apps.forEach((item: Record<string, any>) => {
|
systemStore?.apps.forEach((item: Record<string, any>) => {
|
||||||
routers.push({
|
if (addonRouters[item.key]) {
|
||||||
path: addonRouters[item.key] ? addonRouters[item.key].path : '',
|
addonRouters[item.key].name = addonIndexRoute[item.key]
|
||||||
meta: {
|
routers.push(addonRouters[item.key])
|
||||||
icon: addonRouters[item.key]?.meta.icon || 'element-Setting',
|
}
|
||||||
addon: item.key,
|
|
||||||
title: item.title,
|
|
||||||
app: item.app,
|
|
||||||
show: true
|
|
||||||
},
|
|
||||||
original_name: item.key,
|
|
||||||
name: addonIndexRoute[item.key]
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
oneMenuData.value.unshift(...routers)
|
oneMenuData.value.unshift(...routers)
|
||||||
}
|
}
|
||||||
@ -110,6 +108,10 @@ if (systemStore?.apps.length > 1) {
|
|||||||
const oneMenuActive = ref(route.matched[1].name)
|
const oneMenuActive = ref(route.matched[1].name)
|
||||||
|
|
||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
|
if (route.meta.attr != '') {
|
||||||
|
oneMenuActive.value = route.matched[2].name
|
||||||
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
|
} else {
|
||||||
// 多应用
|
// 多应用
|
||||||
if (systemStore?.apps.length > 1) {
|
if (systemStore?.apps.length > 1) {
|
||||||
twoMenuData.value = route.matched[1].children
|
twoMenuData.value = route.matched[1].children
|
||||||
@ -130,6 +132,19 @@ watch(route, () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// 让二级菜单默认展开
|
||||||
|
const menuOption = ref([])
|
||||||
|
watch(twoMenuData.value, () => {
|
||||||
|
menuOption.value = [];
|
||||||
|
if(twoMenuData.value && Object.values(twoMenuData.value).length){
|
||||||
|
let data = cloneDeep(twoMenuData.value);
|
||||||
|
for(let key in data){
|
||||||
|
menuOption.value.push(data[key].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -27,9 +27,9 @@
|
|||||||
<!-- 预览 只有站点时展示-->
|
<!-- 预览 只有站点时展示-->
|
||||||
<!-- <i class="iconfont iconlingdang-xianxing cursor-pointer px-[8px]" :title="t('newInfo')" v-if="appType == 'admin'"></i>-->
|
<!-- <i class="iconfont iconlingdang-xianxing cursor-pointer px-[8px]" :title="t('newInfo')" v-if="appType == 'admin'"></i>-->
|
||||||
<!-- 切换语言 -->
|
<!-- 切换语言 -->
|
||||||
<div class="navbar-item flex items-center h-full cursor-pointer">
|
<!-- <div class="navbar-item flex items-center h-full cursor-pointer">-->
|
||||||
<switch-lang />
|
<!-- <switch-lang />-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
<!-- 切换全屏 -->
|
<!-- 切换全屏 -->
|
||||||
<!-- <div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
|
<!-- <div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
|
||||||
<icon name="iconfont-icontuichuquanping" v-if="isFullscreen" />
|
<icon name="iconfont-icontuichuquanping" v-if="isFullscreen" />
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user