fix admin

This commit is contained in:
CQ 2025-10-31 09:22:14 +08:00
parent 08d14372e1
commit 8cb683f8fe
21 changed files with 1206 additions and 576 deletions

View File

@ -62,6 +62,8 @@ declare module '@vue/runtime-core' {
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElStatistic: typeof import('element-plus/es')['ElStatistic']
ElStep: typeof import('element-plus/es')['ElStep']

View File

@ -95,3 +95,10 @@ export function syncSiteWeapp(params: Record<string, any>) {
export function getAuthRecord(params: Record<string, any>) {
return request.get('wxoplatform/authorization/record', { params })
}
/**
*
*/
export function cancelAuthorization(params: Record<string, any>) {
return request.post('wxoplatform/authorization/cancel', params, { showSuccessMessage: true })
}

View File

@ -45,5 +45,11 @@
"failReason": "失败原因",
"appVersionReleaseTips": "发布后无法再对该版本进行修改,确定要发布该版本吗?",
"appVersionDeleteTips": "确定要删除该版本吗?",
"upgradeType": "升级方式"
"upgradeType": "升级方式",
"seeBuildLog": "查看打包日志",
"buildLog": "打包日志",
"authTips": "上传代码需先绑定授权码如果已有授权请先进行绑定没有授权可到niucloud官网购买云服务之后再进行操作",
"toBind": "绑定授权",
"toNiucloud": "去niucloud官网",
"siteAuthTips": "上传代码需先绑定授权码,请联系平台管理员进行绑定"
}

View File

@ -9,7 +9,7 @@
<el-button type="primary" class="w-[100px]" @click="addEvent">
{{ t('addMenu') }}
</el-button>
<el-button class="w-[100px]" @click="refreshMenu">
<el-button class="w-[100px]" :loading="refreshLoading" @click="refreshMenu">
{{ t('initializeMenu') }}
</el-button>
</div>
@ -82,6 +82,7 @@ const getMenuList = () => {
}
getMenuList()
//
const refreshLoading = ref(false)
const refreshMenu = () => {
ElMessageBox.confirm(h('div', null, [
h('p', null, t('initializeMenuTipsOne')),
@ -93,9 +94,11 @@ const refreshMenu = () => {
// type: 'warning'
}
).then(() => {
refreshLoading.value = true
menuRefresh({}).then(res => {
location.reload()
refreshLoading.value = false
}).catch(() => {
refreshLoading.value = false
})
}).catch(() => {
})

View File

@ -22,7 +22,7 @@
</el-alert>
<div class="mt-[20px]">
<el-button type="primary" @click="addEvent">{{ t('addAppVersion') }}</el-button>
<el-button type="primary" @click="addEvent" :disabled="loading">{{ t('addAppVersion') }}</el-button>
</div>
<div class="mt-[10px]">
@ -41,7 +41,11 @@
<el-table-column prop="platform_name" :label="t('platform')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="status_name" :label="t('status')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="status_name" :label="t('status')" min-width="120" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<el-button link :loading="row.status == 'creating'">{{ row.status_name }}</el-button>
</template>
</el-table-column>
<el-table-column prop="status" :label="t('isForcedUpgradeTitle')" min-width="120" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
@ -63,7 +67,7 @@
<template #default="{ row }">
<el-button type="primary" link v-if="row.release_time == 0" @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link v-if="row.status == 'upload_success'" @click="releaseEvent(row)">{{ t('release') }}</el-button>
<el-button type="primary" link v-if="row.status == 'create_fail'" @click="handleFailReason(row)">{{ t('failReason') }}</el-button>
<el-button type="primary" link v-if="row.status == 'creating'" @click="seeBuildLog(row)">{{ t('seeBuildLog') }}</el-button>
<el-button type="primary" link v-if="row.package_path && row.upgrade_type != 'market'" @click="downloadEvent(row)">{{ t('download') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
@ -86,21 +90,31 @@
</el-scrollbar>
</el-dialog>
</div>
<el-dialog v-model="showDialog" :title="t('buildLog')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :before-close="dialogClose">
<div class="h-[370px]">
<terminal ref="terminalRef" :name="`upgrade-${terminalId}`" context="" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd" />
</div>
</el-dialog>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import { img, getAppType } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import { getVersionList, getBuildLog, deleteVersion, releaseVersion } from '@/app/api/app'
import Edit from '@/app/views/channel/app/components/app-version-edit.vue'
import { useRoute, useRouter } from 'vue-router'
import { Terminal, TerminalFlash } from 'vue-web-terminal'
import 'vue-web-terminal/lib/theme/dark.css'
import { getAuthInfo } from '@/app/api/module'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const activeName = ref('/channel/app/version')
const terminalRef: any = ref(null)
const appVersionTable = reactive({
page: 1,
limit: 10,
@ -111,6 +125,18 @@ const appVersionTable = reactive({
platfrom: ''
}
})
const showDialog = ref(false)
const loading = ref(true)
const authCode = ref('')
getAuthInfo().then(res => {
if (res.data.data && res.data.data.auth_code) {
authCode.value = res.data.data.auth_code
}
loading.value = false
}).catch(() => {
loading.value = false
})
const handleClick = (val: any) => {
router.push({ path: activeName.value })
@ -146,6 +172,10 @@ const editAppVersionDialog: Record<string, any> | null = ref(null)
* 添加app版本管理
*/
const addEvent = () => {
if (!authCode.value) {
authElMessageBox()
return
}
editAppVersionDialog.value.setFormData()
editAppVersionDialog.value.showDialog = true
}
@ -192,20 +222,70 @@ const releaseEvent = (data: any) => {
})
}
let buildLog = []
const getAppBuildLogFn = (key: string) => {
getBuildLog(key).then(res => {
if (res.data) {
if (res.data.status == '') {
if (showDialog.value) {
if (!buildLog.length) {
terminalRef.value.execute('clear')
terminalRef.value.execute('开始打包')
}
res.data.build_log.data[0].forEach((item) => {
if (!buildLog.includes(item.action)) {
terminalRef.value.pushMessage({ content: `${item.action}` })
buildLog.push(item.action)
}
})
}
setTimeout(() => {
getAppBuildLogFn(key)
}, 2000)
} else {
if (res.data.status == 'fail' && showDialog.value) {
terminalRef.value.pushMessage({ content: res.data.fail_reason, class: 'error' })
} else {
showDialog.value = false
}
loadAppVersionList()
buildLog = []
}
}
})
}
const seeBuildLog = () => {
showDialog.value = true;
}
/**
* 升级进度动画
*/
let flashInterval: any = 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++] }
}
}
}
const failReason = ref('')
const failReasonDialogVisible = ref(false)
const handleFailReason = (data: any) => {
@ -222,6 +302,28 @@ const resetForm = (formEl: FormInstance | undefined) => {
formEl.resetFields()
loadAppVersionList()
}
const authElMessageBox = () => {
if (getAppType() == 'admin') {
ElMessageBox.confirm(
t('authTips'),
t('warning'),
{
distinguishCancelAndClose: true,
confirmButtonText: t('toBind'),
cancelButtonText: t('toNiucloud')
}
).then(() => {
router.push({ path: '/app/authorize' })
}).catch((action: string) => {
if (action === 'cancel') {
window.open('https://www.niucloud.com/app')
}
})
} else {
ElMessageBox.alert(t('siteAuthTips'), t('warning'))
}
}
</script>
<style lang="scss" scoped>

View File

@ -45,6 +45,24 @@
<template v-if="oplatformConfig.app_id && oplatformConfig.app_secret">
<el-button type="primary" @click="router.push('/channel/weapp/config')">{{ weappConfig.app_id ? t("seeConfig") : t("weappSettingBtn") }}</el-button>
<el-button type="primary" plain @click="authBindWeapp">{{ weappConfig.is_authorization ? t("refreshAuth") : t("authWeapp") }}</el-button>
<template v-if="weappConfig.is_authorization">
<el-button type="primary" plain @click="cencelAuth" >{{ t("取消授权") }}</el-button>
<el-tooltip class="box-item" effect="light" placement="top">
<el-icon color="#666" size="15px" class="ml-[5px]">
<QuestionFilled />
</el-icon>
<template #content>
<div>
<div>
非通过API创建的开放平台账号需要登录微信公众品平台进行取消授权操作
</div>
<div>
微信公众平台 <a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://mp.weixin.qq.com/cgi-bin/loginpage">https://mp.weixin.qq.com/cgi-bin/loginpage</a>
</div>
</div>
</template>
</el-tooltip>
</template>
</template>
<template v-else>
<el-button type="primary" @click="router.push('/channel/weapp/config')">{{ t("weappSettingBtn") }}</el-button>
@ -107,9 +125,10 @@ import { onMounted, onUnmounted, ref } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import { getWeappConfig } from '@/app/api/weapp'
import { getAuthorizationUrl } from '@/app/api/wxoplatform'
import { getAuthorizationUrl ,cancelAuthorization} from '@/app/api/wxoplatform'
import { getWxoplatform } from '@/app/api/sys'
import { useRoute, useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus'
const route = useRoute()
const router = useRouter()
@ -158,6 +177,24 @@ const authBindWeapp = () => {
window.open(data)
})
}
const repeat = ref(false)
const cencelAuth = () => {
ElMessageBox.confirm(t('确认取消授权吗?'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
if (repeat.value) return
repeat.value = true
cancelAuthorization({}).then(() => {
repeat.value = false
}).catch(() => {
repeat.value = false
})
})
}
</script>
<style lang="scss" scoped></style>

View File

@ -14,13 +14,32 @@
</el-tabs>
<div class="mt-[20px]" v-if="!weappConfig.is_authorization">
<el-button type="primary" @click="insert" :loading="uploading" :disabled="loading">{{ t('cloudRelease') }}</el-button>
<el-button type="primary" @click="openDialog" :loading="uploading" :disabled="loading">{{ t('cloudRelease') }}</el-button>
<el-button @click="localInsert" :disabled="loading">{{ t('localRelease') }}</el-button>
</div>
<div class="mt-[20px]" v-else>
<el-button type="primary" @click="againUpload" :loading="uploading" :disabled="loading">{{ t('uploadWeapp') }}</el-button>
</div>
<div class="text-[14px] mt-[15px]">
<div class="flex items-center">
<div v-if="weappTableData.version_info.release_version">
线上版本: <span class="mr-10 text-primary">{{ weappTableData.version_info.release_version }}</span>
</div>
<div v-if="weappTableData.version_info.release_time">
发布时间: <span >{{ weappTableData.version_info.release_time }}</span>
</div>
</div>
<div class="flex items-center">
<div v-if="weappTableData.version_info.exp_version" class="mt-2">
体验版本: <span class="mr-10 text-primary">{{ weappTableData.version_info.exp_version }}</span>
</div>
<div v-if="weappTableData.version_info.exp_time" class="mt-2">
过期时间: <span >{{ weappTableData.version_info.exp_time }}</span>
</div>
</div>
</div>
<el-table class="mt-[15px]" :data="weappTableData.data" v-loading="weappTableData.loading" size="default">
<template #empty>
<span>{{ t('emptyData') }}</span>
@ -71,6 +90,42 @@
<el-button @click="dialogVisible = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="insert">
{{ t('confirm') }}
</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="cloudVersionDialogVisible" :title="t('codeDownTwoDesc')" width="600px" :before-close="handleCloseCloudVersion">
<el-form ref="cloudRuleFormRef" :model="form" :rules="formRules" label-width="120px">
<el-form-item prop="type" :label="t('版本号类型')">
<div>
<el-radio-group v-model="form.type">
<el-radio :label="1">{{ t('默认') }}</el-radio>
<el-radio :label="2">{{ t('自定义') }}</el-radio>
</el-radio-group>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">默认为列表版本号递增自定义则为手动输入版本号进行上传首位必须大于1</div>
</div>
</el-form-item>
<el-form-item prop="version" :label="t('code')" v-if="form.type == 2">
<div class="flex items-end">
<el-form-item prop="code1">
<el-input v-model.number="form.code1" class="!w-[70px]" :placeholder="t('codePlaceholder')" />
</el-form-item>
<span class="mx-[10px]">.</span>
<el-form-item prop="code2">
<el-input v-model.number="form.code2" class="!w-[70px]" :placeholder="t('codePlaceholder')" />
</el-form-item>
<span class="mx-[10px]">.</span>
<el-form-item prop="code3">
<el-input v-model.number="form.code3" class="!w-[70px]" :placeholder="t('codePlaceholder')" />
</el-form-item>
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseCloudVersion">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save">
{{ t('confirm') }}
</el-button>
</span>
</template>
@ -116,19 +171,22 @@ const weappTableData:{
limit: number,
total: number,
loading: boolean,
data: AnyObject
data: AnyObject,
version_info: AnyObject
} = reactive({
page: 1,
limit: 10,
total: 0,
loading: false,
data: []
data: [],
version_info: {}
})
const form = ref({
desc: '',
code: '',
path: '',
content: ''
type:1,
version: '',
code1: '1',
code2: '0',
code3: '0'
})
const uploadSuccessShowDialog = ref(false)
const authCode = ref('')
@ -161,7 +219,62 @@ const handleClick = (val: any) => {
router.push({ path: activeName.value })
}
const ruleFormRef = ref<any>(null)
const cloudRuleFormRef = ref<any>(null)
//
const formRules = reactive({
code1: [
{
validator: (rule: any, value: number, callback: any) => {
if (value < 1) {
callback(new Error(t('必须大于1')));
} else {
callback();
}
},
trigger: 'blur'
}
],
code2: [
{
validator: (rule: any, value: number, callback: any) => {
if (value < 0) {
callback(new Error(t('必须大于0')));
} else {
callback();
}
},
trigger: 'blur'
}
],
code3: [
{
validator: (rule: any, value: number, callback: any) => {
if (value < 0) {
callback(new Error(t('必须大于0')));
} else {
callback();
}
},
trigger: 'blur'
}
],
version:[
{
required: true,
validator: (rule: any, value: string, callback: any) => {
if(form.value.type == 2){
if(!form.value.code1 || !form.value.code2 || !form.value.code3){
callback(new Error(t('请填写版本号')));
}else{
callback();
}
}else{
callback();
}
}
}
]
});
/**
* 获取版本列表
*/
@ -177,6 +290,7 @@ const getWeappVersionListFn = (page: number = 1) => {
weappTableData.data = res.data.data
weappTableData.total = res.data.total
if (page == 1 && weappTableData.data.length && weappTableData.data[0].status == 0) getWeappUploadLogFn(weappTableData.data[0].task_key)
weappTableData.version_info = res.data.version_info
}).catch(() => {
weappTableData.loading = false
})
@ -184,10 +298,56 @@ const getWeappVersionListFn = (page: number = 1) => {
getWeappVersionListFn()
const openDialog = () => {
if (!authCode.value) {
authElMessageBox()
return
}
if (!weappConfig.value.app_id) {
configElMessageBox()
return
}
form.value = {
type:1,
version: '',
code1: '1',
code2: '0',
code3: '0'
}
cloudVersionDialogVisible.value = true
}
const handleClose = () => {
ruleFormRef.value.clearValidate()
}
const cloudVersionDialogVisible = ref(false)
const handleCloseCloudVersion = () => {
cloudVersionDialogVisible.value = false
form.value = {
type:1,
version: '',
code1: '1',
code2: '0',
code3: '0'
}
}
const save = () => {
cloudRuleFormRef.value.validate((valid: boolean) => {
if (valid) {
if (form.value.type == 2) {
form.value.version = `${form.value.code1}.${form.value.code2}.${form.value.code3}`
}
delete form.value.code1
delete form.value.code2
delete form.value.code3
delete form.value.type
cloudVersionDialogVisible.value = false
insert()
}
})
}
const uploading = ref(false)
const insert = () => {
if (!authCode.value) {
@ -206,7 +366,7 @@ const insert = () => {
setWeappVersion(form.value).then(res => {
getWeappVersionListFn()
getWeappPreviewImage()
getWeappPreviewImage()
uploading.value = false
}).catch(() => {
uploading.value = false

View File

@ -63,6 +63,20 @@
<el-switch v-model="diyStore.global.bottomTabBar.isShow" />
<div class="text-sm text-gray-400">{{ t('tabbarSwitchTips') }}</div>
</el-form-item>
<el-form-item :label="t('选择导航')">
<tabbar-select-popup v-model="diyStore.global.bottomTabBar.designNav" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap" v-if="diyStore.global.copyright.control">
<h3 class="mb-[10px]">{{ t('版权信息内容') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('版权信息')" class="display-block">
<el-switch v-model="diyStore.global.copyright.isShow" />
<div class="text-sm text-gray-400">{{ t('此处控制当前页面版权信息是否显示') }}</div>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
@ -178,6 +192,7 @@ import { t } from '@/lang'
import { watch, ref } from 'vue'
import useDiyStore from '@/stores/modules/diy'
import { img } from '@/utils/common'
import tabbarSelectPopup from './tabbar-select-popup.vue'
const diyStore = useDiyStore()

View File

@ -0,0 +1,211 @@
<template>
<div>
<div @click="show">
<slot>
<el-input v-model="selectData.title" :placeholder="t('请选择底部导航')" readonly class="link-input">
<template #suffix>
<div @click.stop="clear">
<el-icon v-if="selectData.key">
<Close />
</el-icon>
<el-icon v-else>
<ArrowRight />
</el-icon>
</div>
</template>
</el-input>
</slot>
</div>
<el-dialog v-model="showDialog" :title="t('底部导航选择')" width="850px" :destroy-on-close="true" :close-on-click-modal="false">
<el-table class="" :data="tableData.data" size="large" v-loading="tableData.loading" height="400px">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column min-width="7%">
<template #default="{ row }">
<el-checkbox v-model="row.checked" @change="handleCheckChange($event,row)" />
</template>
</el-table-column>
<el-table-column prop="title" :label="t('title')" min-width="30%" >
<template #default="{ row }">
<span>{{ row.info.title }}</span>
</template>
</el-table-column>
<el-table-column prop="key" :label="t('key')" min-width="30%"/>
<el-table-column :label="t('type')" min-width="30%">
<template #default="{ row }">
<span>{{ row.info.type === 'app' ? t('app') : t('addon') }}</span>
</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="loadBottomNavList()" @current-change="loadBottomNavList" />
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import { ref, reactive, nextTick,computed } from 'vue'
import { FormInstance, ElMessage } from "element-plus";
import { getDiyBottomList } from '@/app/api/diy'
import { cloneDeep } from 'lodash-es'
const prop = defineProps({
modelValue: {
type: Object,
default: () => {
return {
key: '',
title: '',
}
}
},
ignore: {
type: Array,
default: []
}
})
const clear = () => {
selectData.value = {
key: '',
title: ''
}
setTimesSelected()
}
const emit = defineEmits(['update:modelValue', 'confirm', 'success'])
const selectData: any = computed({
get() {
return prop.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
const searchFormRef = ref<FormInstance>()
const timeListTableRef = ref()
const showDialog = ref(false)
const show = () => {
showDialog.value = true
loadBottomNavList()
}
const tableData: any = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
})
//
const loadBottomNavList = (page: number = 1) => {
tableData.loading = true
tableData.page = page
getDiyBottomList({}).then(res => {
tableData.loading = false
const len = Math.ceil(res.data.length / tableData.limit)
const data = cloneDeep(res.data)
const dataGather = []
for (let i = 0; i < len; i++) {
dataGather[i] = data.splice(0, tableData.limit)
}
tableData.data = dataGather[tableData.page - 1]
tableData.data.forEach((item: any) => {
item.checked = item.key == selectData.value.key
})
tableData.total = res.data.length
setTimesSelected();
}).catch(() => {
tableData.loading = false
})
}
loadBottomNavList()
const handleCheckChange = (isSelect: any, row: any) => {
if (isSelect) {
selectData.value = {
key: row.key,
title: row.info.title
};
} else {
selectData.value = {
key: '',
title: ''
};
}
setTimesSelected()
}
// //
const setTimesSelected = () => {
nextTick(() => {
for (let i = 0; i < tableData.data.length; i++) {
tableData.data[i].checked = false
if (selectData.value.key == tableData.data[i].key) {
tableData.data[i].checked = true
selectData.value.key = tableData.data[i].key
selectData.value.title = tableData.data[i].info.title
}
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadBottomNavList()
}
const save = () => {
if (selectData.value.key == '') {
ElMessage({
type: 'warning',
message: `${ t('请选择底部导航') }`
})
return;
}
selectData.value = {
key: selectData.value.key,
title: selectData.value.title
}
showDialog.value = false;
}
defineExpose({
show,
showDialog,
})
</script>
<style lang="scss" scoped>
.form-item-wrap {
margin-right: 10px !important;
margin-bottom: 10px !important;
&.last-child {
margin-right: 0 !important;
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="flex items-center justify-center">
<el-card class="box-card !border-none profile-data w-[1280px] mt-[20px] loading-box" shadow="never" v-loading="loading" >
<el-card class="box-card !border-none profile-data w-[1280px] mt-[20px] loading-box" shadow="never">
<!-- <div> -->
<div class="box-border">
<div class="bg-[#fff] mb-[20px] rounded-[6px] relative banner-box" v-if="showBanner" @mouseenter="hovering = 'banner'" @mouseleave="hovering = ''">
@ -11,7 +11,21 @@
</span>
<div class="flex h-[156px]">
<div class="w-full h-full ">
<el-carousel :interval="3000" height="156px" class="rounded-[6px]">
<!-- 轮播图加载中 -->
<div v-if="loading" class="skeleton-loading h-full flex items-center justify-center">
<el-skeleton
style="width: 100%"
:loading="loading"
animated
:throttle="{ leading: 500, initVal: true }"
>
<template #template>
<el-skeleton-item variant="image" style="width: 100%; height: 156px;" />
</template>
</el-skeleton>
</div>
<el-carousel v-else :interval="3000" height="156px" class="rounded-[6px]">
<!-- <el-carousel-item >
<div class="h-full index-carousel" @click="toApplication">
<img :src="img('static/resource/images/banner_1.png')" alt="" class="w-full h-full cursor-pointer">
@ -99,86 +113,121 @@
</div>
</div>
</div>
<div v-if="showNiuCloud && appList.length > 0" class="mt-[50px] relative" @mouseenter="hovering = 'niuCloud'" @mouseleave="hovering = ''">
<span class="absolute right-0 top-[-5px] text-[#999] hover:text-red-500 cursor-pointer z-10" v-if="hovering === 'niuCloud'">
<el-icon class="icon" :size="20" color="#7b7b7b" @click="closeModule('niuCloud')">
<CircleCloseFilled />
</el-icon>
</span>
<p class="text-[18px] text-[#1D1F3A]">NIUCLOUD生态精选应用推荐</p>
<!-- <div class="flex justify-between mt-[20px]">
<div class="flex">
<div :class="['flex items-center text-[14px] h-[32px] rounded-full px-[20px] mr-[24px] text-[#fff] bg-[#AFB1C8] hover:bg-[#7B7E9A] cursor-pointer', { '!text-[#fff] !bg-[#7B7E9A]': activeName === 'installed' }]" @click="activeNameTabFn('installed')">{{ t("全部") }}</div>
<div :class="['flex items-center text-[14px] h-[32px] rounded-full px-[20px] mr-[24px] text-[#fff] bg-[#AFB1C8] hover:bg-[#7B7E9A] cursor-pointer', { '!text-[#fff] !bg-[#7B7E9A]': activeName === 'uninstalled' }]" @click="activeNameTabFn('uninstalled')">{{ t("商城") }}</div>
<div :class="['flex items-center text-[14px] h-[32px] rounded-full px-[20px] mr-[24px] text-[#fff] bg-[#AFB1C8] hover:bg-[#7B7E9A] cursor-pointer', { '!text-[#fff] !bg-[#7B7E9A]': activeName === 'all' }]" @click="activeNameTabFn('all')">{{ t("数字人") }}</div>
<div :class="['flex items-center text-[14px] h-[32px] rounded-full px-[20px] mr-[24px] text-[#fff] bg-[#AFB1C8] hover:bg-[#7B7E9A] cursor-pointer', { '!text-[#fff] !bg-[#7B7E9A]': activeName === 'all1' }]" @click="activeNameTabFn('all1')">{{ t("拼团") }}</div>
</div>
</div> -->
<div class="mt-[20px]">
<div class="grid grid-cols-5 gap-4">
<div v-for="(item,index) in appList.slice(0,5)" :key="index" @click="toApplicationDetail(item)" class="bg-[#EDEEF4] rounded-[8px] overflow-hidden text-[#666] cursor-pointer hover:shadow-xl transition-shadow duration-300 border-[1px] border-[#EDEEF4]">
<img :src="img(item.app_logo)" alt="" class="w-full rounded-t-[6px]" >
<div class="bg-[#fff] p-[10px]">
<div class="text-[16px] text-[#1D1F3A] mb-[10px]">
<span class="using-hidden">{{ item.app_name }}</span>
</div>
<div class="text-[12px] text-[#4F516D]">
<span class="using-hidden">{{ item.app_desc }}</span>
</div>
<div class="text-[12px] mt-[20px] flex justify-between">
<div class="flex items-center">
<img :src="img(item.developer.headimg)" alt="" class="w-[18px] h-[18px] rounded-full mr-[6px]" v-if="item.developer.headimg">
<img src="@/app/assets/images/member_head.png" alt="" class="w-[18px] h-[18px] rounded-full mr-[6px]" v-else>
<span class="text-[#4F516D] text-[12px] using-hidden">{{ item.developer.nickname }}</span>
<template v-if="loading">
<div v-if="showNiuCloud" class="mt-[50px] relative" @mouseenter="hovering = 'niuCloud'" @mouseleave="hovering = ''">
<span class="absolute right-0 top-[-5px] text-[#999] hover:text-red-500 cursor-pointer z-10" v-if="hovering === 'niuCloud'">
<el-icon class="icon" :size="20" color="#7b7b7b" @click="closeModule('niuCloud')">
<CircleCloseFilled />
</el-icon>
</span>
<p class="text-[18px] text-[#1D1F3A]">NIUCLOUD生态精选应用推荐</p>
<!-- 应用列表加载中 -->
<div v-if="loading" class="mt-[20px] grid grid-cols-5 gap-4">
<div v-for="i in 5" :key="i" class="bg-[#EDEEF4] rounded-[8px] overflow-hidden">
<el-skeleton
style="width: 240px"
:loading="loading"
animated
:throttle="{ leading: 500, initVal: true }"
>
<template #template>
<el-skeleton-item variant="image" style="width: 240px; height: 265px" />
<div style="padding: 14px">
<el-skeleton-item variant="h3" style="width: 50%" />
<div
style="
display: flex;
align-items: center;
justify-items: space-between;
margin-top: 16px;
height: 16px;
"
>
<el-skeleton-item variant="text" style="margin-right: 16px" />
<el-skeleton-item variant="text" style="width: 30%" />
</div>
<div class="flex items-center">
<div class="mr-[10px]">
<span class="iconfont iconchakan !text-[12px] mr-[8px] !text-[#4F516D]"></span>
<span class="text-[#4F516D] text-[12px]">{{ item.visit_num}}</span>
</div>
</template>
</el-skeleton>
</div>
</div>
</div>
</template>
<template v-else>
<div v-if="showNiuCloud && appList.length >0" class="mt-[50px] relative" @mouseenter="hovering = 'niuCloud'" @mouseleave="hovering = ''">
<span class="absolute right-0 top-[-5px] text-[#999] hover:text-red-500 cursor-pointer z-10" v-if="hovering === 'niuCloud'">
<el-icon class="icon" :size="20" color="#7b7b7b" @click="closeModule('niuCloud')">
<CircleCloseFilled />
</el-icon>
</span>
<p class="text-[18px] text-[#1D1F3A]">NIUCLOUD生态精选应用推荐</p>
<div class="mt-[20px]">
<div class="grid grid-cols-5 gap-4">
<div v-for="(item,index) in appList.slice(0,5)" :key="index" @click="toApplicationDetail(item)" class="bg-[#EDEEF4] rounded-[8px] overflow-hidden text-[#666] cursor-pointer hover:shadow-xl transition-shadow duration-300 border-[1px] border-[#EDEEF4]">
<img :src="img(item.app_logo)" alt="" class="w-full rounded-t-[6px]" >
<div class="bg-[#fff] p-[10px]">
<div class="text-[16px] text-[#1D1F3A] mb-[10px]">
<span class="using-hidden">{{ item.app_name }}</span>
</div>
<div class="text-[12px] text-[#4F516D]">
<span class="using-hidden">{{ item.app_desc }}</span>
</div>
<div class="text-[12px] mt-[20px] flex justify-between">
<div class="flex items-center">
<img :src="img(item.developer.headimg)" alt="" class="w-[18px] h-[18px] rounded-full mr-[6px]" v-if="item.developer.headimg">
<img src="@/app/assets/images/member_head.png" alt="" class="w-[18px] h-[18px] rounded-full mr-[6px]" v-else>
<span class="text-[#4F516D] text-[12px] using-hidden">{{ item.developer.nickname }}</span>
</div>
<div>
<span class="iconfont iconxiaoliang !text-[12px] mr-[8px] !text-[#4F516D]"></span>
<span class="text-[#4F516D] text-[12px]">{{ item.sale_num }}</span>
<div class="flex items-center">
<div class="mr-[10px]">
<span class="iconfont iconchakan !text-[12px] mr-[8px] !text-[#4F516D]"></span>
<span class="text-[#4F516D] text-[12px]">{{ item.visit_num}}</span>
</div>
<div>
<span class="iconfont iconxiaoliang !text-[12px] mr-[8px] !text-[#4F516D]"></span>
<span class="text-[#4F516D] text-[12px]">{{ item.sale_num }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<div class="text-[18px] mt-[50px] mb-[15px]">{{ t("dataSummarize") }}</div>
<el-card class="box-card !border-none profile-data" shadow="never">
<el-row :gutter="20" class="top">
<el-col>
<el-card shadow="never" @click="toHref('site/manage','1')" class="cursor-pointer min-w-[180px] first-con">
<div class="text-[20px] font-bold">{{ statInfo.today_data.norma_site_count }}</div>
<div class="text-[20px] font-bold">{{ statInfo.today_data.norma_site_count || 0 }}</div>
<div class="text-[14px] mb-[9px] text-[#4F516D]">{{ t("normalSiteSum") }}</div>
</el-card>
</el-col>
<el-col>
<el-card shadow="never" @click="toHref('site/manage','1')" class="cursor-pointer min-w-[180px] first-con">
<div class="text-[20px] font-bold">{{ statInfo.today_data.week_expire_site_count }}</div>
<div class="text-[20px] font-bold">{{ statInfo.today_data.week_expire_site_count|| 0 }}</div>
<div class="text-[14px] mb-[9px] text-[#4F516D]">{{ t("weekExpireSiteCount") }}</div>
</el-card>
</el-col>
<el-col>
<el-card shadow="never" @click="toHref('site/manage','2')" class="cursor-pointer min-w-[180px] first-con">
<div class="text-[20px] font-bold">{{ statInfo.today_data.expire_site_count }}</div>
<div class="text-[20px] font-bold">{{ statInfo.today_data.expire_site_count|| 0 }}</div>
<div class="text-[14px] mb-[9px] text-[#4F516D]">{{ t("expireSiteSum") }}</div>
</el-card>
</el-col>
<el-col>
<el-card shadow="never" @click="toHref('/app_manage/app_store','uninstalled')" class="cursor-pointer min-w-[180px] first-con">
<div class="text-[20px] font-bold">{{ statInfo.app.app_no_installed_count }}</div>
<div class="text-[20px] font-bold">{{ statInfo.app.app_no_installed_count|| 0 }}</div>
<div class="text-[14px] mb-[9px] text-[#4F516D]">{{ t("noInstallAppSun") }}</div>
</el-card>
</el-col>
<el-col>
<el-card shadow="never" @click="toHref('/app_manage/app_store','installed')" class="cursor-pointer min-w-[180px] first-con">
<div class="text-[20px] font-bold">{{ statInfo.app.app_installed_count }}</div>
<div class="text-[20px] font-bold">{{ statInfo.app.app_installed_count|| 0 }}</div>
<div class="text-[14px] mb-[9px] text-[#4F516D]">{{ t("installAppSun") }}</div>
</el-card>
</el-col>

View File

@ -76,9 +76,7 @@ const formRules = computed(() => {
const setFormData = async () => {
loading.value = true
const service_data = await (await getMap()).data
formData.key = service_data.key
formData.is_open = service_data.is_open
formData.valid_time = service_data.valid_time
Object.assign(formData, service_data)
loading.value = false
}
setFormData()

View File

@ -1,86 +1,86 @@
<template>
<el-dialog v-model="showDialog" :title="formData.uid ? t('updateUser') : t('addUser')" width="750px" :destroy-on-close="true">
<el-scrollbar>
<div class="max-h-[60vh]">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" autocomplete="off" v-loading="loading">
<el-form-item :label="t('username')" prop="username">
<el-input v-model.trim="formData.username" clearable :placeholder="t('usernamePlaceholder')" class="input-width" :readonly="formData.uid" :disabled="formData.uid" @click="realnameInput = false" @blur="realnameInput = true" />
</el-form-item>
<el-dialog v-model="showDialog" :title="formData.uid ? t('updateUser') : t('addUser')" width="750px" :destroy-on-close="true">
<el-scrollbar>
<div class="max-h-[60vh]">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" autocomplete="off" v-loading="loading">
<el-form-item :label="t('username')" prop="username">
<el-input v-model.trim="formData.username" clearable :placeholder="t('usernamePlaceholder')" class="input-width" :readonly="formData.uid" :disabled="formData.uid" @click="realnameInput = false" @blur="realnameInput = true" />
</el-form-item>
<el-form-item :label="t('headImg')">
<upload-image v-model="formData.head_img" />
</el-form-item>
<el-form-item :label="t('headImg')">
<upload-image v-model="formData.head_img" />
</el-form-item>
<el-form-item :label="t('手机号')" prop="mobile">
<el-input v-model.trim="formData.mobile" :placeholder="t('请输入手机号')" clearable class="input-width" maxlength="11" show-word-limit />
</el-form-item>
<el-form-item :label="t('手机号')" prop="mobile">
<el-input v-model.trim="formData.mobile" :placeholder="t('请输入手机号')" clearable class="input-width" maxlength="11" show-word-limit />
</el-form-item>
<el-form-item :label="t('userRealName')" prop="real_name">
<el-input v-model.trim="formData.real_name" :placeholder="t('userRealNamePlaceholder')" :readonly="realnameInput" @click="realnameInput = false" @blur="realnameInput = true" clearable class="input-width" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('userRealName')" prop="real_name">
<el-input v-model.trim="formData.real_name" :placeholder="t('userRealNamePlaceholder')" :readonly="realnameInput" @click="realnameInput = false" @blur="realnameInput = true" clearable class="input-width" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('password')" prop="password">
<el-input v-model.trim="formData.password" :class="passwordType == 'text' ? '' :'displayPass'" clearable :placeholder="t('passwordPlaceholder')" class="input-width" :readonly="passwordInput" @click="passwordInput = false" @blur="passwordInput = true" >
<template #suffix>
<el-icon @click="togglePasswordVisibility" class="cursor-pointer">
<component :is=" passwordType === 'password' ? 'Hide' : 'View'" />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item :label="t('password')" prop="password">
<el-input v-model.trim="formData.password" :class="passwordType == 'text' ? '' :'displayPass'" clearable :placeholder="t('passwordPlaceholder')" class="input-width" :readonly="passwordInput" @click="passwordInput = false" @blur="passwordInput = true" >
<template #suffix>
<el-icon @click="togglePasswordVisibility" class="cursor-pointer">
<component :is=" passwordType === 'password' ? 'Hide' : 'View'" />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item :label="t('confirmPassword')" prop="confirm_password">
<el-input v-model.trim="formData.confirm_password" :class="confirmPasswordType == 'text' ? '' :'displayPass'" :placeholder="t('confirmPasswordPlaceholder')" clearable class="input-width" :readonly="confirmPasswordInput" @click="confirmPasswordInput = false" @blur="confirmPasswordInput = true" >
<template #suffix>
<el-icon @click="toggleConfirmPasswordVisibility" class="cursor-pointer">
<component :is=" confirmPasswordType === 'password' ? 'Hide' : 'View'" />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item :label="t('confirmPassword')" prop="confirm_password">
<el-input v-model.trim="formData.confirm_password" :class="confirmPasswordType == 'text' ? '' :'displayPass'" :placeholder="t('confirmPasswordPlaceholder')" clearable class="input-width" :readonly="confirmPasswordInput" @click="confirmPasswordInput = false" @blur="confirmPasswordInput = true" >
<template #suffix>
<el-icon @click="toggleConfirmPasswordVisibility" class="cursor-pointer">
<component :is=" confirmPasswordType === 'password' ? 'Hide' : 'View'" />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item :label="t('userCreateSiteLimit')" v-if="!formData.uid && Object.keys(siteGroup).length" prop="create_site_limit">
<div>
<div>{{ t('siteGroup') }}</div>
<el-checkbox-group v-model="formData.group_ids" @change="groupSelect">
<el-checkbox v-for="item in siteGroup" :label="item.group_id">{{ item.group_name }}</el-checkbox>
</el-checkbox-group>
<el-form-item :label="t('userCreateSiteLimit')" v-if="!formData.uid && Object.keys(siteGroup).length" prop="create_site_limit">
<div>
<div>{{ t('siteGroup') }}</div>
<el-checkbox-group v-model="formData.group_ids" @change="groupSelect">
<el-checkbox v-for="item in siteGroup" :label="item.group_id">{{ item.group_name }}</el-checkbox>
</el-checkbox-group>
</div>
<div class="w-full">
<div>{{ t('userCreateSiteLimit') }}</div>
<el-table :data="formData.create_site_limit" size="large" class="w-full">
<el-table-column :label="t('siteGroup')" :show-overflow-tooltip="true">
<template #default="{ row }">
{{ siteGroup[row.group_id] ? siteGroup[row.group_id].group_name : '' }}
</template>
</el-table-column>
<el-table-column :label="t('createSiteNum')">
<template #default="{ $index }">
<el-input v-model.number.trim="formData.create_site_limit[$index].num">
</el-input>
</template>
</el-table-column>
<el-table-column :label="t('siteMonth')">
<template #default="{ $index }">
<el-input v-model.number.trim="formData.create_site_limit[$index].month">
<template #append>{{ t('month') }}</template>
</el-input>
</template>
</el-table-column>
</el-table>
</div>
</el-form-item>
</el-form>
</div>
<div class="w-full">
<div>{{ t('userCreateSiteLimit') }}</div>
<el-table :data="formData.create_site_limit" size="large" class="w-full">
<el-table-column :label="t('siteGroup')" :show-overflow-tooltip="true">
<template #default="{ row }">
{{ siteGroup[row.group_id] ? siteGroup[row.group_id].group_name : '' }}
</template>
</el-table-column>
<el-table-column :label="t('createSiteNum')">
<template #default="{ $index }">
<el-input v-model.number.trim="formData.create_site_limit[$index].num">
</el-input>
</template>
</el-table-column>
<el-table-column :label="t('siteMonth')">
<template #default="{ $index }">
<el-input v-model.number.trim="formData.create_site_limit[$index].month">
<template #append>{{ t('month') }}</template>
</el-input>
</template>
</el-table-column>
</el-table>
</div>
</el-form-item>
</el-form>
</div>
</el-scrollbar>
</el-scrollbar>
<template #footer>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
@ -97,135 +97,135 @@ const userStore = useUserStore()
const showDialog = ref(false)
const loading = ref(true)
const formData = ref({
uid: 0,
username: '',
password: '',
head_img: '',
mobile: '',
real_name: '',
confirm_password: '',
create_site_limit: [],
group_ids: []
uid: 0,
username: '',
password: '',
head_img: '',
mobile: '',
real_name: '',
confirm_password: '',
create_site_limit: [],
group_ids: []
})
const siteGroup = ref({})
const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
username: [
{ required: true, message: t('usernamePlaceholder'), trigger: 'blur' }
],
password: [
{ required: userStore.userInfo && userStore.userInfo.is_super_admin == true, message: t('passwordPlaceholder'), trigger: 'blur' }
],
mobile: [
{ required: true, message: t('请输入手机号'), trigger: 'blur' },
{
validator: (rule: any, value: string, callback: any) => {
if (!Test.mobile(value)) {
callback(new Error(t('手机号格式错误')))
} else {
callback()
}
},
trigger: 'blur'
}
],
real_name: [
{ required: true, message: t('userRealNamePlaceholder'), trigger: 'blur' }
],
confirm_password: [
{ required: userStore.userInfo && userStore.userInfo.is_super_admin == true, message: t('confirmPasswordPlaceholder'), trigger: 'blur' },
{
validator: (rule: any, value: string, callback: any) => {
if (value != formData.value.password) callback(new Error(t('confirmPasswordError')))
else callback()
},
trigger: 'blur'
}
],
create_site_limit: [
{
validator: (rule: any, value: string, callback: any) => {
if (formData.value.uid) callback()
let verify = true
for (let i = 0; i < formData.value.create_site_limit.length; i++) {
const item = formData.value.create_site_limit[i]
if (Test.empty(item.num)) {
callback(t('siteNumPlaceholder'))
verify = false
break
return {
username: [
{ required: true, message: t('usernamePlaceholder'), trigger: 'blur' }
],
password: [
{ required: userStore.userInfo && userStore.userInfo.is_super_admin == true, message: t('passwordPlaceholder'), trigger: 'blur' }
],
mobile: [
{ required: true, message: t('请输入手机号'), trigger: 'blur' },
{
validator: (rule: any, value: string, callback: any) => {
if (!Test.mobile(value)) {
callback(new Error(t('手机号格式错误')))
} else {
callback()
}
},
trigger: 'blur'
}
if (item.num < 1) {
callback(t('siteNumCannotLtOne'))
verify = false
break
],
real_name: [
{ required: true, message: t('userRealNamePlaceholder'), trigger: 'blur' }
],
confirm_password: [
{ required: userStore.userInfo && userStore.userInfo.is_super_admin == true, message: t('confirmPasswordPlaceholder'), trigger: 'blur' },
{
validator: (rule: any, value: string, callback: any) => {
if (value != formData.value.password) callback(new Error(t('confirmPasswordError')))
else callback()
},
trigger: 'blur'
}
if (Test.empty(item.month)) {
callback(t('siteMonthPlaceholder'))
verify = false
break
],
create_site_limit: [
{
validator: (rule: any, value: string, callback: any) => {
if (formData.value.uid) callback()
let verify = true
for (let i = 0; i < formData.value.create_site_limit.length; i++) {
const item = formData.value.create_site_limit[i]
if (Test.empty(item.num)) {
callback(t('siteNumPlaceholder'))
verify = false
break
}
if (item.num < 1) {
callback(t('siteNumCannotLtOne'))
verify = false
break
}
if (Test.empty(item.month)) {
callback(t('siteMonthPlaceholder'))
verify = false
break
}
if (item.month < 0) {
callback(t('siteMonthCannotLtOne'))
verify = false
break
}
}
if (verify) callback()
}
}
if (item.month < 0) {
callback(t('siteMonthCannotLtOne'))
verify = false
break
}
}
if (verify) callback()
}
}
]
}
]
}
})
getSiteGroupAll().then(({ data }) => {
const list: any = {}
data.forEach((item: any) => {
list[item.group_id] = item
})
siteGroup.value = list
const list: any = {}
data.forEach((item: any) => {
list[item.group_id] = item
})
siteGroup.value = list
})
const setFormData = (uid: number = 0) => {
if (uid) {
getUserInfo(uid).then(({ data }) => {
formData.value.uid = data.uid
formData.value.username = data.username
formData.value.mobile = data.mobile
formData.value.real_name = data.real_name
formData.value.head_img = data.head_img
loading.value = false
showDialog.value = true
})
} else {
formData.value = {
uid: 0,
username: '',
password: '',
head_img: '',
mobile: '',
real_name: '',
confirm_password: '',
create_site_limit: [],
group_ids: []
if (uid) {
getUserInfo(uid).then(({ data }) => {
formData.value.uid = data.uid
formData.value.username = data.username
formData.value.mobile = data.mobile
formData.value.real_name = data.real_name
formData.value.head_img = data.head_img
loading.value = false
showDialog.value = true
})
} else {
formData.value = {
uid: 0,
username: '',
password: '',
head_img: '',
mobile: '',
real_name: '',
confirm_password: '',
create_site_limit: [],
group_ids: []
}
loading.value = false
showDialog.value = true
}
loading.value = false
showDialog.value = true
}
}
const emits = defineEmits(['complete'])
const groupSelect = (groupIds: number[]) => {
const list:any = []
groupIds.forEach(item => {
list.push({
group_id: item,
num: 1,
month: 1
const list:any = []
groupIds.forEach(item => {
list.push({
group_id: item,
num: 1,
month: 1
})
})
})
formData.value.create_site_limit = list
formData.value.create_site_limit = list
}
/**
@ -233,22 +233,22 @@ const groupSelect = (groupIds: number[]) => {
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
const save = formData.value.uid ? editUser : addUser
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
const save = formData.value.uid ? editUser : addUser
save(formData.value).then(() => {
loading.value = false
showDialog.value = false
emits('complete')
}).catch(() => {
loading.value = false
})
}
})
save(formData.value).then(() => {
loading.value = false
showDialog.value = false
emits('complete')
}).catch(() => {
loading.value = false
})
}
})
}
const realnameInput = ref(true)
@ -258,23 +258,23 @@ const confirmPasswordInput = ref(true)
const passwordType = ref('password')
const togglePasswordVisibility = () => {
passwordType.value = passwordType.value === 'password' ? 'text' : 'password'
passwordType.value = passwordType.value === 'password' ? 'text' : 'password'
}
const confirmPasswordType = ref('password')
const toggleConfirmPasswordVisibility = () => {
confirmPasswordType.value = confirmPasswordType.value === 'password' ? 'text' : 'password'
confirmPasswordType.value = confirmPasswordType.value === 'password' ? 'text' : 'password'
}
defineExpose({
showDialog,
setFormData
showDialog,
setFormData
})
</script>
<style lang="scss" scoped>
.displayPass {
:deep(.el-input__inner){
:deep(.el-input__inner){
-webkit-text-security: disc !important;
}
}

View File

@ -1,99 +1,99 @@
<template>
<!--站点用户-->
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<!--站点用户-->
<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-button type="primary" class="w-[100px]" @click="userEditRef.setFormData()">{{ t('addUser') }}</el-button>
</div>
</div>
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<div>
<el-button type="primary" class="w-[100px]" @click="userEditRef.setFormData()">{{ t('addUser') }}</el-button>
</div>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="userTableData.searchParam" ref="searchFormRef" class="search-form">
<el-form-item prop="username">
<el-input v-model.trim="userTableData.searchParam.username" :placeholder="t('userNamePlaceholder')" />
</el-form-item>
<!-- <el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="userTableData.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item> -->
<el-form-item prop="last_time">
<el-date-picker v-model="userTableData.searchParam.last_time" type="datetimerange"
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="userTableData.searchParam" ref="searchFormRef" class="search-form">
<el-form-item prop="username">
<el-input v-model.trim="userTableData.searchParam.username" :placeholder="t('userNamePlaceholder')" />
</el-form-item>
<!-- <el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="userTableData.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadUserList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
</el-form-item> -->
<el-form-item prop="last_time">
<el-date-picker v-model="userTableData.searchParam.last_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadUserList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div>
<el-table :data="userTableData.data" size="large" v-loading="userTableData.loading">
<template #empty>
<span>{{ !userTableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('headImg')" width="100" align="left">
<template #default="{ row }">
<div class="w-[54px] h-[54px] flex items-center justify-center">
<img v-if="row.head_img" :src="img(row.head_img)" class="w-[54px] rounded-full" />
<img v-else src="@/app/assets/images/member_head.png" class="w-[54px] rounded-full" />
</div>
</template>
</el-table-column>
<el-table-column prop="username" :label="t('accountNumber')" min-width="120" show-overflow-tooltip />
<el-table-column prop="mobile" :label="t('手机号')" min-width="120" show-overflow-tooltip />
<el-table-column prop="real_name" :label="t('userRealName')" min-width="120" show-overflow-tooltip />
<el-table-column prop="site_num" :label="t('siteNum')" min-width="120" show-overflow-tooltip align="center" />
<!-- <el-table-column prop="create_time" :label="t('createTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column> -->
<el-table-column prop="last_time" :label="t('lastLoginTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.last_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('lastLoginIP')" min-width="180" align="center">
<template #default="{ row }">
{{ row.last_ip || '' }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" fixed="right" width="180">
<template #default="{ row }">
<el-button type="primary" link @click="detailEvent(row.uid)">{{ t('detail') }}</el-button>
<template v-if="!row.is_super_admin">
<el-button type="primary" link @click="editEvent(row.uid)" >{{ t('edit') }}</el-button>
<el-button type="primary" link @click="detailEvent(row.uid, 'userCreateSiteLimit')" >{{ t('userCreateSiteLimit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.uid)" >{{ t('delete') }}</el-button>
</template>
<!-- <div class="manage-option text-right ">
<template v-if="!row.is_super_admin">
<el-button type="primary" link @click="editEvent(row.uid)" >{{ t('edit') }}</el-button>
<el-button type="primary" link @click="detailEvent(row.uid, 'userCreateSiteLimit')" >{{ t('userCreateSiteLimit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.uid)" >{{ t('delete') }}</el-button>
</template>
</div> -->
</template>
</el-table-column>
</el-table>
<div>
<el-table :data="userTableData.data" size="large" v-loading="userTableData.loading">
<template #empty>
<span>{{ !userTableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('headImg')" width="100" align="left">
<template #default="{ row }">
<div class="w-[54px] h-[54px] flex items-center justify-center">
<img v-if="row.head_img" :src="img(row.head_img)" class="w-[54px] rounded-full" />
<img v-else src="@/app/assets/images/member_head.png" class="w-[54px] rounded-full" />
</div>
</template>
</el-table-column>
<el-table-column prop="username" :label="t('accountNumber')" min-width="120" show-overflow-tooltip />
<el-table-column prop="mobile" :label="t('手机号')" min-width="120" show-overflow-tooltip />
<el-table-column prop="real_name" :label="t('userRealName')" min-width="120" show-overflow-tooltip />
<el-table-column prop="site_num" :label="t('siteNum')" min-width="120" show-overflow-tooltip align="center" />
<!-- <el-table-column prop="create_time" :label="t('createTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column> -->
<el-table-column prop="last_time" :label="t('lastLoginTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.last_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('lastLoginIP')" min-width="180" align="center">
<template #default="{ row }">
{{ row.last_ip || '' }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" fixed="right" width="180">
<template #default="{ row }">
<el-button type="primary" link @click="detailEvent(row.uid)">{{ t('detail') }}</el-button>
<template v-if="!row.is_super_admin">
<el-button type="primary" link @click="editEvent(row.uid)" >{{ t('edit') }}</el-button>
<el-button type="primary" link @click="detailEvent(row.uid, 'userCreateSiteLimit')" >{{ t('userCreateSiteLimit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.uid)" >{{ t('delete') }}</el-button>
</template>
<!-- <div class="manage-option text-right ">
<template v-if="!row.is_super_admin">
<el-button type="primary" link @click="editEvent(row.uid)" >{{ t('edit') }}</el-button>
<el-button type="primary" link @click="detailEvent(row.uid, 'userCreateSiteLimit')" >{{ t('userCreateSiteLimit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.uid)" >{{ t('delete') }}</el-button>
</template>
</div> -->
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="userTableData.page" v-model:page-size="userTableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="userTableData.total"
@size-change="loadUserList()" @current-change="loadUserList" />
</div>
</div>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="userTableData.page" v-model:page-size="userTableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="userTableData.total"
@size-change="loadUserList()" @current-change="loadUserList" />
</div>
</div>
</el-card>
</el-card>
<user-edit ref="userEditRef" @complete="loadUserList()"/>
</div>
<user-edit ref="userEditRef" @complete="loadUserList()"/>
</div>
</template>
<script lang="ts" setup>
@ -112,47 +112,47 @@ const pageName = route.meta.title
const userEditRef = ref(null)
const userTableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
username: '',
site_name: '',
// create_time: [],
last_time: []
}
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
username: '',
site_name: '',
// create_time: [],
last_time: []
}
})
const searchFormRef = ref<FormInstance>()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
if (!formEl) return
formEl.resetFields()
loadUserList()
formEl.resetFields()
loadUserList()
}
/**
* 获取用户列表
*/
const loadUserList = (page: number = 1) => {
userTableData.loading = true
userTableData.page = page
userTableData.loading = true
userTableData.page = page
getUserList({
page: userTableData.page,
limit: userTableData.limit,
...userTableData.searchParam
getUserList({
page: userTableData.page,
limit: userTableData.limit,
...userTableData.searchParam
}).then(res => {
userTableData.loading = false
userTableData.data = res.data.data
userTableData.total = res.data.total
}).catch(() => {
userTableData.loading = false
})
}).then(res => {
userTableData.loading = false
userTableData.data = res.data.data
userTableData.total = res.data.total
}).catch(() => {
userTableData.loading = false
})
}
loadUserList()
@ -160,7 +160,7 @@ loadUserList()
* 查看详情
*/
const detailEvent = (uid: number, tab: string = '') => {
router.push({ path: '/admin/site/user_info', query: { uid, tab } })
router.push({ path: '/admin/site/user_info', query: { uid, tab } })
}
/**
@ -168,69 +168,69 @@ const detailEvent = (uid: number, tab: string = '') => {
* @param uid
*/
const editEvent = (uid: number) => {
userEditRef.value.setFormData(uid)
userEditRef.value.setFormData(uid)
}
/**
* 删除用户
*/
const deleteEvent = (uid: number) => {
ElMessageBox.confirm(t('userDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteUser(uid).then(res => {
loadUserList()
}).catch(() => {
ElMessageBox.confirm(t('userDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteUser(uid).then(res => {
loadUserList()
}).catch(() => {
})
})
})
}
</script>
<style lang="scss" scoped>
:deep(.el-table tr td) {
height: 100px !important;
height: 100px !important;
}
:deep(.el-input__wrapper){
box-shadow: none !important;
border-radius: 4px !important;
border: 1px solid #D1D5DB !important;
height: 32px !important;
box-shadow: none !important;
border-radius: 4px !important;
border: 1px solid #D1D5DB !important;
height: 32px !important;
}
:deep(.el-select__wrapper){
box-shadow: none !important;
border-radius: 4px !important;
border: 1px solid #D1D5DB !important;
height: 32px !important;
box-shadow: none !important;
border-radius: 4px !important;
border: 1px solid #D1D5DB !important;
height: 32px !important;
}
:deep(.el-button){
border-radius: 4px !important;
border-radius: 4px !important;
}
/* 设置 el-select 的 placeholder 颜色 */
:deep(.search-form .el-select__placeholder.is-transparent) {
color: #C4C7DA;
font-size: 12px;
color: #C4C7DA;
font-size: 12px;
}
/* 设置 el-select 选中后的颜色 */
:deep(.search-form .el-select__placeholder) {
color: #4F516D;
font-size: 12px;
color: #4F516D;
font-size: 12px;
}
/* 设置 el-input 的 placeholder 颜色 */
:deep(.search-form .el-input__inner::placeholder) {
color: #C4C7DA;
font-size: 12px;
color: #C4C7DA;
font-size: 12px;
}
/* 设置 el-input 输入内容后的颜色 */
:deep(.search-form .el-input__inner) {
color: #4F516D;
font-size: 12px;
color: #4F516D;
font-size: 12px;
}
/* 设置 el-date-picker 的 placeholder 颜色 */
@ -246,43 +246,43 @@ const deleteEvent = (uid: number) => {
}
.manage-option {
line-height: 50px;
padding: 0 30px;
position: absolute;
right: 0;
width: 100vw;
bottom: 0;
background-color: #f4f6f9;
transition: all .3s;
box-shadow: 0 4px 4px rgba(220, 220, 220, .3);
opacity: 0;
z-index: 999;
white-space: nowrap;
line-height: 50px;
padding: 0 30px;
position: absolute;
right: 0;
width: 100vw;
bottom: 0;
background-color: #f4f6f9;
transition: all .3s;
box-shadow: 0 4px 4px rgba(220, 220, 220, .3);
opacity: 0;
z-index: 999;
white-space: nowrap;
}
/* 当行被 hover 时 */
:deep(.el-table__row:hover) {
position: relative;
z-index: 10;
position: relative;
z-index: 10;
}
/* 当行被 hover 时,其下的单元格允许溢出 */
:deep(.el-table__row:hover .el-table__cell) {
overflow: visible;
overflow: visible;
}
/* 当行被 hover 时,显示 manage-option 并调整其位置 */
:deep(.el-table__row:hover .manage-option) {
opacity: 1;
bottom: -51px;
opacity: 1;
bottom: -51px;
}
:deep(.el-table__fixed-body-wrapper:hover),
:deep(.el-table__fixed-body-wrapper .el-table__row:hover) {
z-index: 10;
:deep(.el-table__fixed-body-wrapper:hover),
:deep(.el-table__fixed-body-wrapper .el-table__row:hover) {
z-index: 10;
}
:deep(.el-table__fixed-body-wrapper .el-table__row:hover .el-table__cell) {
overflow: visible;
overflow: visible;
}
</style>

View File

@ -1,135 +1,135 @@
<template>
<!--用户详情-->
<div class="main-container" v-loading="loading">
<!--用户详情-->
<div class="main-container" v-loading="loading">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('userInfo') }}</h3>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('userInfo') }}</h3>
<el-row :gutter="20" class="mt-[20px] mb-[20px]">
<el-col :span="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('uid') }}</span>
<span class="text-[14px] text-[#666666]">
<el-row :gutter="20" class="mt-[20px] mb-[20px]">
<el-col :span="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('uid') }}</span>
<span class="text-[14px] text-[#666666]">
{{ detail.uid }}
</span>
</el-col>
<el-col :span="6" :offset="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('username') }}</span>
<span class="text-[14px] text-[#666666]">
</el-col>
<el-col :span="6" :offset="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('username') }}</span>
<span class="text-[14px] text-[#666666]">
{{ detail.username }}
</span>
</el-col>
</el-row>
<el-row :gutter="20" class="mb-[20px]">
<el-col :span="6" >
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('realname') }}</span>
<span class="text-[14px] text-[#666666]">
</el-col>
</el-row>
<el-row :gutter="20" class="mb-[20px]">
<el-col :span="6" >
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('realname') }}</span>
<span class="text-[14px] text-[#666666]">
{{ detail.real_name || '--' }}
</span>
</el-col>
<el-col :span="6" :offset="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('手机号') }}</span>
<span class="text-[14px] text-[#666666]">
</el-col>
<el-col :span="6" :offset="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('手机号') }}</span>
<span class="text-[14px] text-[#666666]">
{{ detail.mobile || '--' }}
</span>
</el-col>
</el-row>
<el-row :gutter="20" class="mb-[20px]">
<el-col :span="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('addTime') }}</span>
<span class="text-[14px] text-[#666666]">
</el-col>
</el-row>
<el-row :gutter="20" class="mb-[20px]">
<el-col :span="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('addTime') }}</span>
<span class="text-[14px] text-[#666666]">
{{ detail.create_time }}
</span>
</el-col>
<el-col :span="6" :offset="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginTime') }}</span>
<span class="text-[14px] text-[#666666]">
</el-col>
<el-col :span="6" :offset="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginTime') }}</span>
<span class="text-[14px] text-[#666666]">
{{ detail.last_time || '' }}
</span>
</el-col>
</el-row>
<el-row :gutter="20" class="mb-[20px]">
<el-col :span="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginIP') }}</span>
<span class="text-[14px] text-[#666666]">
</el-col>
</el-row>
<el-row :gutter="20" class="mb-[20px]">
<el-col :span="6">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginIP') }}</span>
<span class="text-[14px] text-[#666666]">
{{ detail.last_ip || '' }}
</span>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-tabs v-model="currTab">
<el-tab-pane :label="t('siteInfo')" name="siteInfo">
<el-table :data="detail.roles" size="large">
<el-table-column prop="site_id" :label="t('siteId')" width="100px" />
<el-table-column prop="site_name" :label="t('siteName')" />
<el-table-column prop="is_admin" :label="t('isAdmin')" min-width="180" align="center">
<template #default="{ row }">
{{ row.is_admin ? t('yes') : t('no') }}
</template>
</el-table-column>
<el-table-column :label="t('status')" min-width="80" align="center">
<template #default="{ row }">
<template v-if="row.site_status_name">
<el-tag class="ml-2" type="success" v-if="row.site_status == 1">{{ row.site_status_name }}</el-tag>
<el-tag class="ml-2" type="error" v-else-if="row.site_status == 3">
{{ row.site_status_name }}
</el-tag>
<el-tag class="ml-2" type="error" v-else>
{{ row.site_status_name }}
</el-tag>
</template>
<el-tag class="ml-2" type="error" v-else>
{{ t('siteEmpty') }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="expire_time" :label="t('expireTime')" />
<el-table-column :label="t('operation')" align="right" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="siteInfo(row)" v-if="row.site_status_name">{{ t('info') }}</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane :label="t('userCreateSiteLimit')" name="userCreateSiteLimit" v-if="!detail.is_super_admin">
<div class="flex justify-end mb-[16px]">
<el-button type="primary" @click="createSiteLimitRef.setFormData()">{{ t('addSserCreateSiteLimit') }}</el-button>
</div>
<el-table :data="userCreateSiteLimit" size="large">
<el-table-column :label="t('siteGroup')">
<template #default="{ row }">
{{ siteGroup[row.group_id] ? siteGroup[row.group_id].group_name : '' }}
</template>
</el-table-column>
<el-table-column :label="t('createdSiteNum')">
<template #default="{ row }">
{{ siteGroup[row.group_id] ? siteGroup[row.group_id].site_num : 0 }}
</template>
</el-table-column>
<el-table-column prop="num" :label="t('createSiteNum')" align="center"/>
<el-table-column prop="month" :label="t('createSiteTimeLimit')" align="center">
<template #default="{ row }">
{{ row.month }}{{ t('month') }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="createSiteLimitRef.setFormData(row.id)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteCreateSiteTimeLimit(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-tabs v-model="currTab">
<el-tab-pane :label="t('siteInfo')" name="siteInfo">
<el-table :data="detail.roles" size="large">
<el-table-column prop="site_id" :label="t('siteId')" width="100px" />
<el-table-column prop="site_name" :label="t('siteName')" />
<el-table-column prop="is_admin" :label="t('isAdmin')" min-width="180" align="center">
<template #default="{ row }">
{{ row.is_admin ? t('yes') : t('no') }}
</template>
</el-table-column>
<el-table-column :label="t('status')" min-width="80" align="center">
<template #default="{ row }">
<template v-if="row.site_status_name">
<el-tag class="ml-2" type="success" v-if="row.site_status == 1">{{ row.site_status_name }}</el-tag>
<el-tag class="ml-2" type="error" v-else-if="row.site_status == 3">
{{ row.site_status_name }}
</el-tag>
<el-tag class="ml-2" type="error" v-else>
{{ row.site_status_name }}
</el-tag>
</template>
<el-tag class="ml-2" type="error" v-else>
{{ t('siteEmpty') }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="expire_time" :label="t('expireTime')" />
<el-table-column :label="t('operation')" align="right" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="siteInfo(row)" v-if="row.site_status_name">{{ t('info') }}</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane :label="t('userCreateSiteLimit')" name="userCreateSiteLimit" v-if="!detail.is_super_admin">
<div class="flex justify-end mb-[16px]">
<el-button type="primary" @click="createSiteLimitRef.setFormData()">{{ t('addSserCreateSiteLimit') }}</el-button>
</div>
<el-table :data="userCreateSiteLimit" size="large">
<el-table-column :label="t('siteGroup')">
<template #default="{ row }">
{{ siteGroup[row.group_id] ? siteGroup[row.group_id].group_name : '' }}
</template>
</el-table-column>
<el-table-column :label="t('createdSiteNum')">
<template #default="{ row }">
{{ siteGroup[row.group_id] ? siteGroup[row.group_id].site_num : 0 }}
</template>
</el-table-column>
<el-table-column prop="num" :label="t('createSiteNum')" align="center"/>
<el-table-column prop="month" :label="t('createSiteTimeLimit')" align="center">
<template #default="{ row }">
{{ row.month }}{{ t('month') }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="createSiteLimitRef.setFormData(row.id)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteCreateSiteTimeLimit(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-card>
<create-site-limit ref="createSiteLimitRef" :site-group="siteGroup" :uid="uid" @complete="getUserCreateSiteLimitFn"/>
</div>
<create-site-limit ref="createSiteLimitRef" :site-group="siteGroup" :uid="uid" @complete="getUserCreateSiteLimitFn"/>
</div>
</template>
<script lang="ts" setup>
@ -147,7 +147,7 @@ const router = useRouter()
const pageName = route.meta.title
const back = () => {
router.push('/admin/site/user')
router.push('/admin/site/user')
}
const uid: number = parseInt(route.query.uid || 0)
const loading = ref(true)
@ -158,22 +158,22 @@ const userCreateSiteLimit = ref([])
const createSiteLimitRef = ref(null)
getUserInfo(uid).then(({ data }) => {
detail.value = data
loading.value = false
detail.value = data
loading.value = false
}).catch()
getUserSiteGroupAll({ uid }).then(({ data }) => {
const list: any = {}
data.forEach((item: any) => {
list[item.group_id] = item
})
siteGroup.value = list
const list: any = {}
data.forEach((item: any) => {
list[item.group_id] = item
})
siteGroup.value = list
})
const getUserCreateSiteLimitFn = () => {
getUserCreateSiteLimit(uid).then(({ data }) => {
userCreateSiteLimit.value = data
})
getUserCreateSiteLimit(uid).then(({ data }) => {
userCreateSiteLimit.value = data
})
}
getUserCreateSiteLimitFn()
@ -182,22 +182,22 @@ getUserCreateSiteLimitFn()
* @param data
*/
const siteInfo = (data: any) => {
router.push({ path: '/admin/site/info', query: { id: data.site_id } })
router.push({ path: '/admin/site/info', query: { id: data.site_id } })
}
const deleteCreateSiteTimeLimit = (id: number) => {
ElMessageBox.confirm(t('createSiteTimeLimitDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
delUserCreateSiteLimit(id).then(() => {
getUserCreateSiteLimitFn()
}).catch(() => {
ElMessageBox.confirm(t('createSiteTimeLimitDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
delUserCreateSiteLimit(id).then(() => {
getUserCreateSiteLimitFn()
}).catch(() => {
})
})
})
}
</script>

View File

@ -24,7 +24,7 @@
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { useRouter , useRoute} from 'vue-router'
import { computed } from 'vue'
import { img } from '@/utils/common'
import menuItem from './menu-item.vue'
@ -32,6 +32,8 @@ import useUserStore from '@/stores/modules/user'
import storage from '@/utils/storage'
const router = useRouter()
const route = useRoute()
const props = defineProps({
routes: {
type: Object,
@ -70,8 +72,13 @@ const handleJump = (routeName: string) => {
if (specialMenuNamesLevel1.includes(routeName)) {
routeName = 'addon_list'
}
//
const query = route.name === routeName
? { refresh: Date.now() } //
: {};
//
router.push({ name: routeName })
router.push({ name: routeName, query });
}
</script>

View File

@ -190,9 +190,13 @@ const handleJump = (routeName: string) => {
if (specialMenuNamesLevel1.value.includes(routeName)) {
routeName = 'addon_list'
}
//
const query = route.name === routeName
? { refresh: Date.now() } //
: {};
//
router.push({ name: routeName })
router.push({ name: routeName, query });
}
watch(route, () => {

View File

@ -43,7 +43,7 @@ const addonIndexRoute = userStore.addonIndexRoute
const menuData = ref<Record<string, any>[]>([])
const addonRouters: Record<string, any> = {}
const logoUrl = computed(() => {
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
return userStore.siteInfo.logo ? userStore.siteInfo.logo : systemStore.website.logo
})
const appList = ref<Record<string, any>[]>([])

View File

@ -183,9 +183,13 @@ const handleJump = (routeName: string) => {
if (specialMenuNamesLevel1.value.includes(routeName)) {
routeName = 'addon_list'
}
//
const query = route.name === routeName
? { refresh: Date.now() } //
: {};
//
router.push({ name: routeName })
router.push({ name: routeName, query });
}
watch(route, () => {

View File

@ -44,7 +44,7 @@ const addonIndexRoute = userStore.addonIndexRoute
const menuData = ref<Record<string, any>[]>([])
const addonRouters: Record<string, any> = {}
const logoUrl = computed(() => {
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
return userStore.siteInfo.logo ? userStore.siteInfo.logo : systemStore.website.logo
})
const appList = ref<Record<string, any>[]>([])

View File

@ -24,13 +24,14 @@
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { useRouter ,useRoute } from 'vue-router'
import { computed } from 'vue'
import menuItem from './menu-item.vue'
import useUserStore from '@/stores/modules/user'
import storage from '@/utils/storage'
const router = useRouter()
const route = useRoute()
const props = defineProps({
routes: {
type: Object,
@ -69,8 +70,13 @@ const handleJump = (routeName: string) => {
if (specialMenuNamesLevel1.includes(routeName)) {
routeName = 'addon_list'
}
//
const query = route.name === routeName
? { refresh: Date.now() } //
: {};
//
router.push({ name: routeName })
router.push({ name: routeName, query });
}
</script>

View File

@ -77,8 +77,17 @@ const useDiyStore = defineStore('diy', {
bottomTabBar: {
control: true, // 是否允许展示编辑
isShow: true, // 是否显示
designNav:{ //类型
title: "", // 标题
key: "", // 组件标识
}
},
// 版权信息
copyright: {
control: true, // 是否允许展示编辑
isShow: false, // 是否显示
},
// 弹框 count不弹出 -1首次弹出 1每次弹出 0
popWindow: {
imgUrl: "",
@ -171,6 +180,16 @@ const useDiyStore = defineStore('diy', {
bottomTabBar: {
control: true, // 是否允许展示编辑
isShow: true, // 是否显示
designNav:{ //类型
title: "", // 标题
key: "", // 组件标识
}
},
// 版权信息
copyright: {
control: true, // 是否允许展示编辑
isShow: true, // 是否显示
},
// 弹框 count不弹出 -1首次弹出 1每次弹出 0