13 Commits

  1. 2
      .gitignore
  2. 136
      package-lock.json
  3. 4
      package.json
  4. 36
      src/api/userPermissions.js
  5. 203
      src/layout/Layout.vue
  6. 2
      src/main.js
  7. 6
      src/router/index.js
  8. 19
      src/store/permission.js
  9. 208
      src/views/UserPermissions/Market.vue
  10. 50
      src/views/UserPermissions/Module.vue
  11. 802
      src/views/UserPermissions/invitedLook.vue
  12. 12
      vite.config.js

2
.gitignore

@ -9,6 +9,8 @@ lerna-debug.log*
node_modules
dist
dist_development
dist_production
dist-ssr
*.local

136
package-lock.json

@ -12,6 +12,7 @@
"axios": "^1.13.2",
"element-plus": "^2.11.8",
"normalize.css": "^8.0.1",
"pinia": "^3.0.4",
"vue": "^3.5.24",
"vue-router": "^4.6.3"
},
@ -984,6 +985,30 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/@vue/devtools-kit": {
"version": "7.7.9",
"resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
"integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.7.9",
"birpc": "^2.3.0",
"hookable": "^5.5.3",
"mitt": "^3.0.1",
"perfect-debounce": "^1.0.0",
"speakingurl": "^14.0.1",
"superjson": "^2.2.2"
}
},
"node_modules/@vue/devtools-shared": {
"version": "7.7.9",
"resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
"integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
"license": "MIT",
"dependencies": {
"rfdc": "^1.4.1"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.24",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz",
@ -1145,6 +1170,15 @@
"proxy-from-env": "^1.1.0"
}
},
"node_modules/birpc": {
"version": "2.9.0",
"resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz",
"integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@ -1170,6 +1204,21 @@
"node": ">= 0.8"
}
},
"node_modules/copy-anything": {
"version": "4.0.5",
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz",
"integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
"license": "MIT",
"dependencies": {
"is-what": "^5.2.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/csstype": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.0.tgz",
@ -1501,6 +1550,24 @@
"node": ">= 0.4"
}
},
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/is-what": {
"version": "5.5.0",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz",
"integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@ -1569,6 +1636,12 @@
"node": ">= 0.6"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@ -1599,6 +1672,12 @@
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==",
"license": "MIT"
},
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -1618,6 +1697,36 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pinia": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz",
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^7.7.7"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.5.0",
"vue": "^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/@vue/devtools-api": {
"version": "7.7.9",
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
"integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
"license": "MIT",
"dependencies": {
"@vue/devtools-kit": "^7.7.9"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@ -1652,6 +1761,12 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},
"node_modules/rollup": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz",
@ -1703,6 +1818,27 @@
"node": ">=0.10.0"
}
},
"node_modules/speakingurl": {
"version": "14.0.1",
"resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/superjson": {
"version": "2.2.6",
"resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz",
"integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
"license": "MIT",
"dependencies": {
"copy-anything": "^4"
},
"engines": {
"node": ">=16"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",

4
package.json

@ -4,8 +4,9 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --host",
"build": "vite build",
"build:dev": "vite build --mode development",
"preview": "vite preview"
},
"dependencies": {
@ -13,6 +14,7 @@
"axios": "^1.13.2",
"element-plus": "^2.11.8",
"normalize.css": "^8.0.1",
"pinia": "^3.0.4",
"vue": "^3.5.24",
"vue-router": "^4.6.3"
},

36
src/api/userPermissions.js

@ -163,3 +163,39 @@ export function setEnvApi(params) {
data: params,
});
}
// 登录-获取权限接口
export function getPermissionApi(params) {
return request({
url: base_url + "/admin/auth/info",
method: "post",
data: params,
});
}
// 用户邀请-获取用户邀请列表接口
export function getInvitedListApi(params) {
return request({
url: base_url + "/admin/invite/list",
method: "post",
data: params,
});
}
// 用户邀请--创建导出
export function exportInvitedApi(params) {
return request({
url: base_url + "/admin/invite/export/create",
method: "post",
data: params,
});
}
// 用户邀请--获取来源下拉框
export function getOriginListApi(params) {
return request({
url: base_url + "/admin/invite/user/origin/detail",
method: "post",
data: params,
});
}

203
src/layout/Layout.vue

@ -1,28 +1,17 @@
<!-- @format -->
<template>
<el-container style="min-height: 100vh;">
<el-aside
class="sidebar"
v-if="!$route.meta.hiddenSidebar"
>
<el-container style="min-height: 100vh">
<el-aside class="sidebar" v-if="!$route.meta.hiddenSidebar">
<!-- 侧边栏头部 -->
<div class="sidebar-header">
<img src="../assets/images/deepChart.png" class="sidebar-logo">
<img src="../assets/images/deepChart.png" class="sidebar-logo" />
<span class="sidebar-title">DeepChart</span>
</div>
<!-- 侧边栏菜单 -->
<el-menu
class="sidebar-menu"
background-color="transparent"
router
:default-active="lastActivePath"
:unique-opened="true"
>
<el-sub-menu
v-for="parentRoute in filteredSidebarRoutes"
:key="parentRoute.name"
:index="`/${parentRoute.path}`"
>
<el-menu class="sidebar-menu" background-color="transparent" router :default-active="lastActivePath" :unique-opened="true">
<el-sub-menu v-for="parentRoute in filteredSidebarRoutes" :key="parentRoute.name" :index="`/${parentRoute.path}`">
<!-- 父目录 -->
<template #title>
<el-icon>
@ -32,13 +21,7 @@
</template>
<!-- 子目录 -->
<el-menu-item
v-for="childRoute in parentRoute.filteredChildren"
:key="childRoute.name"
:index="`/${parentRoute.path}/${childRoute.path}`"
:to="`/${parentRoute.path}/${childRoute.path}`"
class="sidebar-child-container"
>
<el-menu-item v-for="childRoute in parentRoute.filteredChildren" :key="childRoute.name" :index="`/${parentRoute.path}/${childRoute.path}`" :to="`/${parentRoute.path}/${childRoute.path}`" class="sidebar-child-container">
<el-icon class="sidebar-child-icon" />
<span class="sidebar-child-text">{{ childRoute.meta.title }}</span>
</el-menu-item>
@ -46,11 +29,11 @@
</el-menu>
<div class="sidebar-logout" @click="handleLogout">
<el-icon style="bottom: -3px; "><SwitchButton /></el-icon>
<el-icon style="bottom: -3px"><SwitchButton /></el-icon>
退出登录
</div>
<div class="sidebar-set" @click="handleSet">
<el-icon style="bottom: -3px; "><Setting /></el-icon>
<div class="sidebar-set" @click="handleSet" v-if="permission == '2'">
<el-icon style="bottom: -3px"><Setting /></el-icon>
设置
</div>
</el-aside>
@ -59,7 +42,7 @@
<el-dialog v-model="setValue" title="设置" width="500">
<div class="refresh-time-container">
<el-button type="danger">首页刷新时间</el-button>
<el-form-item label="刷新时间" style="margin-top: 20px; margin-bottom: 40px;">
<el-form-item label="刷新时间" style="margin-top: 20px; margin-bottom: 40px">
<el-input-number v-model="refreshTime" :step="1" placeholder="请输入刷新时间" style="width: 200px">
<template #suffix>
<span>s</span>
@ -67,7 +50,7 @@
</el-input-number>
</el-form-item>
<el-button type="danger">Android下载链接配置</el-button>
<el-form-item label="编辑链接" style="margin-top: 20px; margin-bottom: 40px;">
<el-form-item label="编辑链接" style="margin-top: 20px; margin-bottom: 40px">
<el-input v-model="Androidurl" style="width: 300px" placeholder="请输入最新Android下载链接" />
</el-form-item>
</div>
@ -87,18 +70,24 @@
</template>
<script setup>
import { computed, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus';
import { getEnvApi, setEnvApi} from '../api/userPermissions'
import { computed, ref, watch, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { getEnvApi, setEnvApi } from "../api/userPermissions";
import { usePermissionStore } from "../store/permission";
const permissionStore = usePermissionStore();
const router = useRouter()
const route = useRoute()
const router = useRouter();
const route = useRoute();
// token
const token = localStorage.getItem("token");
//permission
const permission = ref("-1");
//
const collectParentRoutes = (routes) => {
let parentRoutes = [];
routes.forEach(route => {
routes.forEach((route) => {
if (route.meta?.isParentNav === true && route.meta?.showSidebar === true) {
parentRoutes.push(route);
}
@ -109,25 +98,50 @@ const collectParentRoutes = (routes) => {
return parentRoutes;
};
const getPermission = async () => {
const result = await getPermissionApi({ token: token });
console.log("result", result);
permission.value = result.permission_level;
};
//
const filteredSidebarRoutes = computed(() => {
const allParentRoutes = collectParentRoutes(router.options.routes);
return allParentRoutes.map(parentRoute => {
const filteredChildren = parentRoute.children?.filter(childRoute =>
childRoute.meta?.showSidebar === true
) || [];
return { ...parentRoute, filteredChildren };
}).filter(parentRoute => parentRoute.filteredChildren.length > 0);
return allParentRoutes
.map((parentRoute) => {
const filteredChildren =
parentRoute.children?.filter((childRoute) => {
//
if (childRoute.meta?.showSidebar !== true) return false;
//
if (permission.value == "2") {
// 2
return true;
} else if (permission.value == "1") {
// 1
return childRoute.name === "invitedLook";
}
//
return false;
}) || [];
return { ...parentRoute, filteredChildren };
})
.filter((parentRoute) => parentRoute.filteredChildren.length > 0);
});
//
const validMenuIndexes = computed(() => {
const indexes = [];
filteredSidebarRoutes.value.forEach(parentRoute => {
filteredSidebarRoutes.value.forEach((parentRoute) => {
//
indexes.push(`/${parentRoute.path}`);
//
parentRoute.filteredChildren.forEach(childRoute => {
parentRoute.filteredChildren.forEach((childRoute) => {
indexes.push(`/${parentRoute.path}/${childRoute.path}`);
});
});
@ -135,7 +149,7 @@ const validMenuIndexes = computed(() => {
});
//
const lastActivePath = ref('');
const lastActivePath = ref("");
// +
watch(
@ -152,37 +166,34 @@ watch(
// 退
const handleLogout = () => {
try {
localStorage.removeItem('token');
router.push('/login');
ElMessage.success('退出登录成功');
localStorage.removeItem("token");
router.push("/login");
ElMessage.success("退出登录成功");
} catch (error) {
ElMessage.error('退出登录失败,请重试');
ElMessage.error("退出登录失败,请重试");
}
};
// token
const token = localStorage.getItem('token')
//
const setValue = ref(false)
const setValue = ref(false);
//
const refreshTime = ref('')
const refreshTime = ref("");
//
const Androidurl = ref('')
const Androidurl = ref("");
//
const handleSet = async() => {
const handleSet = async () => {
const data1 = await getEnvApi({
token: token,
key: 'SYNC_INTERVAL'
})
key: "SYNC_INTERVAL",
});
const data2 = await getEnvApi({
token: token,
key: 'DOWNLOAD_URL'
})
key: "DOWNLOAD_URL",
});
refreshTime.value = data1;
Androidurl.value = data2;
@ -190,27 +201,31 @@ const handleSet = async() => {
};
//
const setEnv = async() => {
try{
const setEnv = async () => {
try {
await setEnvApi({
token: token,
key: 'SYNC_INTERVAL',
value: refreshTime.value
})
key: "SYNC_INTERVAL",
value: refreshTime.value,
});
await setEnvApi({
token: token,
key: 'DOWNLOAD_URL',
value: Androidurl.value
})
key: "DOWNLOAD_URL",
value: Androidurl.value,
});
ElMessage.success('修改成功');
ElMessage.success("修改成功");
setValue.value = false;
} catch (error) {
ElMessage.error('修改失败');
ElMessage.error("修改失败");
setValue.value = false;
}
}
};
onMounted(async () => {
permission.value = await permissionStore.getPermission();
});
</script>
<style scoped>
@ -223,7 +238,7 @@ const setEnv = async() => {
height: calc(100vh - 20px);
flex-shrink: 0;
border-radius: 8px;
background: #FEE6E6;
background: #fee6e6;
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.25);
overflow: hidden;
position: relative;
@ -266,7 +281,7 @@ const setEnv = async() => {
/* 主内容区样式 */
.main-content {
padding: 20px;
background-color: #FEE6E6;
background-color: #fee6e6;
height: calc(100vh - 20px);
margin-left: 50px;
margin-right: 24px;
@ -296,10 +311,10 @@ const setEnv = async() => {
width: 300px !important;
height: 60px !important;
margin-left: 2px !important;
background: #FEE6E6 !important;
background: #fee6e6 !important;
position: relative;
padding: 0 !important;
border-radius: 6.02px !important;
border-radius: 6.02px !important;
box-sizing: border-box !important;
}
@ -308,7 +323,7 @@ const setEnv = async() => {
position: absolute;
left: 48px;
top: 50%;
transform: translateY(-50%);
transform: translateY(-50%);
font-size: 18px !important;
color: inherit;
}
@ -332,13 +347,13 @@ const setEnv = async() => {
/* 子目录选中态样式 */
.sidebar-child-container.is-active {
background: #FFFFFF !important; /* 选中后容器变为白色 */
background: #ffffff !important; /* 选中后容器变为白色 */
}
/* 选中态文字样式 */
.sidebar-child-container.is-active .sidebar-child-text {
color: #FF0000 !important; /* 选中后文字红色 */
font-weight: 500;
color: #ff0000 !important; /* 选中后文字红色 */
font-weight: 500;
}
/* 覆盖Element Plus默认样式 */
@ -348,7 +363,7 @@ const setEnv = async() => {
padding: 0 !important;
}
.el-menu-item {
border: none !important;
border: none !important;
}
.el-menu--vertical .el-menu-item {
width: 300px !important;
@ -357,46 +372,46 @@ const setEnv = async() => {
/* 退出登录 */
.sidebar-logout {
position: absolute;
bottom: 30px;
bottom: 30px;
left: 10%;
color: #1f0303;
font-family: "PingFang SC", sans-serif;
font-size: 21.06px;
font-style: normal;
font-weight: 700;
line-height: 33.1px;
cursor: pointer;
cursor: pointer;
user-select: none;
transition: color 0.2s ease;
transition: color 0.2s ease;
}
/* 退出登录hover效果 */
.sidebar-logout:hover {
color: #FF0000;
color: #ff0000;
}
/* 设置 */
.sidebar-set {
position: absolute;
bottom: 30px;
bottom: 30px;
left: 60%;
color: #1f0303;
font-family: "PingFang SC", sans-serif;
font-size: 21.06px;
font-style: normal;
font-weight: 700;
line-height: 33.1px;
cursor: pointer;
cursor: pointer;
user-select: none;
transition: color 0.2s ease;
transition: color 0.2s ease;
}
/* 设置hover效果 */
.sidebar-set:hover {
color: #FF0000;
color: #ff0000;
}
</style>
</style>

2
src/main.js

@ -1,4 +1,5 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
// 导入 Element Plus 样式和组件
@ -19,6 +20,7 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.use(ElementPlus, {
locale: zhCn,
})
app.use(createPinia())
app.use(router)
app.mount('#app')

6
src/router/index.js

@ -40,6 +40,12 @@ const routes = [
component: () => import('../views/UserPermissions/Module.vue'),
meta: { title: '模块期限', showSidebar: true }
},
{
path: 'invitedLook',
name: 'invitedLook',
component: () => import('../views/UserPermissions/invitedLook.vue'),
meta: { title: '查看被邀请用户', showSidebar: true }
},
// 深度探索--操作日志
{
path: 'logDeepexplore',

19
src/store/permission.js

@ -0,0 +1,19 @@
/** @format */
import { defineStore } from "pinia";
import { ref } from "vue";
import { getPermissionApi } from "../api/userPermissions";
export const usePermissionStore = defineStore("permission", () => {
//permission
const permission = ref("-1");
async function getPermission() {
// token
const token = localStorage.getItem("token");
const result = await getPermissionApi({ token: token });
permission.value = result.permission_level;
return permission.value;
}
return { permission, getPermission };
});

208
src/views/UserPermissions/Market.vue

@ -3,50 +3,67 @@
<!-- 搜索区域 -->
<div class="search-container">
<div class="search-form">
<div class="search-item">
<span class="form-label">账号</span>
<el-input
v-model="searchForm.dccode"
placeholder="请输入账号"
clearable
style="height: 36px; width: 140px;"
/>
</div>
<div class="search-item">
<span class="form-label">姓名</span>
<el-input
v-model="searchForm.dcname"
placeholder="请输入姓名"
clearable
style="height: 36px; width: 180px;"
/>
</div>
<div class="search-item">
<span class="form-label">地区</span>
<el-select
v-model="searchForm.market"
placeholder="请选择地区"
clearable
filterable
style="height: 36px; width: 160px;"
:loading="isRegionLoading"
>
<el-option
v-for="region in regionList"
:key="region.ID"
:label="region.Name"
:value="region.ID"
/>
</el-select>
</div>
<div class="search-item">
<span class="form-label">注册方式</span>
<el-input
v-model="searchForm.register_type"
placeholder="请输入手机号/邮箱"
clearable
style="height: 36px; width: 220px;"
/>
<div class="search-group">
<div class="search-group1">
<div class="search-item">
<span class="form-label">账号</span>
<el-input
v-model="searchForm.dccode"
placeholder="请输入账号"
clearable
style="height: 36px; width: 140px;"
/>
</div>
<div class="search-item">
<span class="form-label">姓名</span>
<el-input
v-model="searchForm.dcname"
placeholder="请输入姓名"
clearable
style="height: 36px; width: 140px;"
/>
</div>
<div class="search-item">
<span class="form-label">来源</span>
<el-select v-model="searchForm.origin_id" placeholder="请选择来源" clearable filterable style="height: 36px; width: 160px" :loading="isOriginLoading">
<el-option v-for="origin in originList" :key="origin.origin_id" :label="origin.origin_name" :value="origin.origin_id" />
</el-select>
</div>
<div class="search-item">
<span class="form-label">地区</span>
<el-select
v-model="searchForm.market"
placeholder="请选择地区"
clearable
filterable
style="height: 36px; width: 160px;"
:loading="isRegionLoading"
>
<el-option
v-for="region in regionList"
:key="region.ID"
:label="region.Name"
:value="region.ID"
/>
</el-select>
</div>
</div>
<div class="search-group2">
<div class="search-item">
<span class="form-label">注册方式</span>
<el-input
v-model="searchForm.register_type"
placeholder="请输入手机号/邮箱"
clearable
style="height: 36px; width: 220px;"
/>
</div>
<div class="search-item">
<span class="form-label">注册时间</span>
<el-date-picker v-model="searchDate" type="datetimerange" :shortcuts="shortcuts" range-separator="" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" />
</div>
</div>
</div>
<div class="button-group">
<el-button type="primary" @click="search">搜索</el-button>
@ -73,15 +90,27 @@
{{ (currentPage - 1) * pageSize + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="jwcode" label="账号" align="center" header-align="center"/>
<el-table-column prop="jwcode" label="账号" align="center" header-align="center" width="120"/>
<el-table-column prop="username" label="姓名" align="center" header-align="center" width="150"/>
<el-table-column prop="country" label="地区" align="center" header-align="center"/>
<el-table-column prop="dialingCode" align="center" header-align="center" width="60"/>
<el-table-column prop="mobiles" label="电话" align="center" header-align="center"/>
<el-table-column prop="emails" label="邮箱" align="center" header-align="center" width="200"/>
<el-table-column prop="regtime" label="注册时间" align="center" header-align="center" sortable="custom" width="180"/>
<el-table-column prop="expire_time" label="到期时间" align="center" header-align="center" sortable="custom" width="180"/>
<el-table-column label="操作" width="180" align="center" header-align="center">
<el-table-column prop="origin_name" label="来源" align="center" header-align="center" width="200"/>
<el-table-column prop="country" label="地区" align="center" header-align="center" width="150"/>
<el-table-column prop="dialingCode" label="手机区号" align="center" header-align="center" width="80"/>
<el-table-column prop="mobiles" label="手机号" align="center" header-align="center" width="200"/>
<!-- <el-table-column prop="emails" label="邮箱" align="center" header-align="center" width="200"/> -->
<el-table-column prop="emails" label="邮箱" align="center" header-align="center" width="150">
<template #default="scope">
<el-tooltip
effect="dark"
:content="scope.row.emails"
placement="top"
>
<span class="ellipsis-text">{{ scope.row.emails }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="regtime" label="注册时间" align="center" header-align="center" sortable="custom" width="200"/>
<el-table-column prop="expire_time" label="到期时间" align="center" header-align="center" sortable="custom" width="200"/>
<el-table-column label="操作" width="180" align="center" header-align="center" fixed="right">
<template #default="scope">
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" @click="handleLog(scope.row.jwcode)">操作日志</el-button>
@ -186,7 +215,7 @@
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { marketListApi, userMListApi, exportMarketApi, exitMApi } from '../../api/userPermissions'
import { marketListApi, userMListApi, getOriginListApi,exportMarketApi, exitMApi } from '../../api/userPermissions'
import router from '../../router';
// token
@ -196,9 +225,12 @@ const token = localStorage.getItem('token')
const searchForm = reactive({
dccode: '',
dcname: '',
origin_id: "",
market: '',
register_type: ''
});
const searchDate = ref([]);
//
const sortProp = ref(null);
@ -217,6 +249,11 @@ const pageSize = ref(10);
const regionList = ref([]);
const isRegionLoading = ref(false);
//
const originList = ref([]);
const isOriginLoading = ref(false);
//
const dialogVisible = ref(false);
@ -397,6 +434,23 @@ const resetForm = () => {
operator.value = '';
};
//
const fetchOriginList = async () => {
try {
isOriginLoading.value = true;
const data = await getOriginListApi({
token: token,
app_form: "en",
});
originList.value = data.list;
} catch (error) {
console.error("获取来源下拉框失败:", error);
originList.value = [];
} finally {
isOriginLoading.value = false;
}
};
//
const fetchRegionList = async () => {
try {
@ -422,8 +476,11 @@ const fetchTableData = async () => {
token: token,
dccode: searchForm.dccode,
dcname: searchForm.dcname,
origin_id: searchForm.origin_id,
market: searchForm.market,
register_type: searchForm.register_type,
start_time: searchDate.value && searchDate.value[0] ? searchDate.value[0] : "",
end_time: searchDate.value && searchDate.value[1] ? searchDate.value[1] : "",
sort_field: sortProp.value,
sort_order: sortOrder.value,
page: currentPage.value,
@ -443,6 +500,7 @@ const fetchTableData = async () => {
// +
onMounted(() => {
fetchOriginList()
fetchRegionList();
fetchTableData();
});
@ -465,8 +523,11 @@ const exportExcel = async () => {
token: token,
dccode: searchForm.dccode,
dcname: searchForm.dcname,
origin_id: searchForm.origin_id,
market: searchForm.market,
register_type: searchForm.register_type,
start_time: searchDate.value && searchDate.value[0] ? searchDate.value[0] : "",
end_time: searchDate.value && searchDate.value[1] ? searchDate.value[1] : "",
sort_field: sortProp.value,
sort_order: sortOrder.value
};
@ -487,8 +548,10 @@ const exportList = () => {
const resetBn = () => {
searchForm.dccode = '';
searchForm.dcname = '';
searchForm.origin_id = '';
searchForm.market = '';
searchForm.register_type = '';
searchDate.value = [];
sortProp.value = null;
sortOrder.value = null;
currentPage.value = 1;
@ -550,16 +613,16 @@ const handleSortChange = (sort) => {
/* 搜索区域 */
.search-container {
display: flex;
height: 67px;
height: auto;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 10px;
gap: 12px;
align-self: stretch;
border-radius: 8px;
background: #FEFAF9;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
padding: 0 15px;
padding: 15px;
margin-bottom: 20px;
}
@ -569,9 +632,29 @@ const handleSortChange = (sort) => {
align-items: center;
width: 100%;
gap: 15px;
flex-wrap: nowrap;
flex-wrap: wrap;
row-gap: 8px;
}
/* 搜索组 */
.search-group {
}
.search-group1{
display: flex;
align-items: center;
gap: 15px;
}
.search-group2{
display: flex;
margin-top: 15px;
align-items: center;
gap: 15px;
}
/* 单个搜索项 */
.search-item {
display: flex;
@ -609,7 +692,7 @@ const handleSortChange = (sort) => {
border-radius: 12px !important;
overflow: hidden !important;
border: 1px solid #e4e7ed !important;
min-height: 750px;
height: 700px;
}
.table-header {
@ -634,6 +717,15 @@ const handleSortChange = (sort) => {
background-color: #fafafa !important;
}
.ellipsis-text {
display: inline-block;
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
/* 分页组件样式 */
.demo-pagination-block {
display: flex;

50
src/views/UserPermissions/Module.vue

@ -2,7 +2,7 @@
<div>
<div class="page-container" v-show="taber == 'DeepMate'">
<!-- 搜索区域 -->
<div class="search-container">
<div class="search-container" @keyup.enter="searchDM">
<div class="search-form">
<div class="search-item">
<span class="form-label">账号</span>
@ -23,6 +23,15 @@
/>
</div>
<div class="search-item">
<span class="form-label">归属</span>
<el-input
v-model="searchFormDM.inviter"
placeholder="请输入归属账号"
clearable
style="height: 36px; width: 180px;"
/>
</div>
<div class="search-item">
<span class="form-label">客户类型</span>
<el-select
v-model="searchFormDM.user_role"
@ -97,6 +106,7 @@
</el-table-column>
<el-table-column prop="dccode" label="账号" align="center" header-align="center"/>
<el-table-column prop="dcname" label="姓名" align="center" header-align="center"/>
<el-table-column prop="inviter" label="归属" align="center" header-align="center"/>
<el-table-column prop="module_name" label="模块名称" align="center" header-align="center"/>
<el-table-column prop="token_num" label="token数量" align="center" header-align="center"/>
<el-table-column prop="updated_at" label="操作时间" align="center" header-align="center" sortable="custom"/>
@ -173,7 +183,7 @@
<div class="page-container" v-show="taber == 'DeepExplore'">
<!-- 搜索区域 -->
<div class="search-containerDE">
<div class="search-containerDE" @keyup.enter="searchDE">
<!-- 输入项区域 -->
<div class="search-formDE">
<div class="search-itemDE">
@ -195,6 +205,15 @@
/>
</div>
<div class="search-itemDE">
<span class="form-labelDE">归属</span>
<el-input
v-model="searchFormDE.inviter"
placeholder="请输入归属账号"
clearable
style="height: 36px; width: 180px;"
/>
</div>
<div class="search-itemDE">
<span class="form-labelDE">客户类型</span>
<el-select
v-model="searchFormDE.user_role"
@ -286,6 +305,7 @@
</el-table-column>
<el-table-column prop="dccode" label="账号" align="center" header-align="center"/>
<el-table-column prop="dcname" label="姓名" align="center" header-align="center"/>
<el-table-column prop="inviter" label="归属" align="center" header-align="center"/>
<el-table-column prop="name" label="指标名称" align="center" header-align="center" width="200">
<template #default="scope">
<el-tooltip
@ -299,7 +319,7 @@
</el-table-column>
<el-table-column prop="created_at" label="开通时间" align="center" header-align="center" sortable="custom" width="200"/>
<el-table-column prop="expire_time" label="到期时间" align="center" header-align="center" sortable="custom" width="200"/>
<el-table-column label="操作" align="center" header-align="center">
<el-table-column label="操作" align="center" header-align="center" width="200">
<template #default="scope">
<el-button type="text" @click="handleEditDE(scope.row)">编辑</el-button>
<el-button type="text" @click="handleDetailsDE(scope.row.dccode)">权限详情</el-button>
@ -510,6 +530,7 @@ const fetchRegionList = async () => {
const searchFormDM = reactive({
dccode: '',
dcname: '',
inviter:'',
market: '',
user_role: ''
});
@ -534,6 +555,7 @@ const DMTableData = async () => {
token: token,
dccode: searchFormDM.dccode,
dcname: searchFormDM.dcname,
inviter: searchFormDM.inviter,
market: searchFormDM.market,
user_role: searchFormDM.user_role,
sort_order: sortOrderDM.value,
@ -582,6 +604,7 @@ const searchDM = () => {
const resetBnDM = () => {
searchFormDM.dccode = '';
searchFormDM.dcname = '';
searchFormDM.inviter = '';
searchFormDM.market = '';
searchFormDM.user_role = '';
sortOrderDM.value = null;
@ -602,6 +625,7 @@ const exportExcelDM = async () => {
token: token,
dccode: searchFormDM.dccode,
dcname: searchFormDM.dcname,
inviter: searchFormDM.inviter,
market: searchFormDM.market,
user_role: searchFormDM.user_role,
sort_order: sortOrderDM.value
@ -784,6 +808,7 @@ const indicatorList = async () => {
const searchFormDE = reactive({
dccode: '',
dcname: '',
inviter: '',
market: '',
user_role: '',
indicator_id: ''
@ -834,6 +859,7 @@ const DETableData = async () => {
token: token,
dccode: searchFormDE.dccode,
dcname: searchFormDE.dcname,
inviter: searchFormDE.inviter,
market: searchFormDE.market,
user_role: searchFormDE.user_role,
indicator_id: searchFormDE.indicator_id,
@ -864,6 +890,7 @@ const searchDE = () => {
const resetBnDE = () => {
searchFormDE.dccode = '';
searchFormDE.dcname = '';
searchFormDE.inviter = '';
searchFormDE.market = '';
searchFormDE.user_role = '';
searchFormDE.indicator_id = '';
@ -880,6 +907,7 @@ const exportExcelDE = async () => {
token: token,
dccode: searchFormDE.dccode,
dcname: searchFormDE.dcname,
inviter: searchFormDE.inviter,
market: searchFormDE.market,
user_role: searchFormDE.user_role,
indicator_id: searchFormDE.indicator_id,
@ -1156,16 +1184,16 @@ const openDetail = async(dccode) => {
/* 搜索区域 */
.search-container {
display: flex;
height: 67px;
height: auto;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 10px;
gap: 12px;
align-self: stretch;
border-radius: 8px;
background: #FEFAF9;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
padding: 0 15px;
padding: 15px;
margin-bottom: 20px;
}
@ -1175,7 +1203,8 @@ const openDetail = async(dccode) => {
align-items: center;
width: 100%;
gap: 15px;
flex-wrap: nowrap;
flex-wrap: wrap;
row-gap: 8px;
}
/* 单个搜索项 */
@ -1199,8 +1228,7 @@ const openDetail = async(dccode) => {
.button-group {
display: flex;
align-items: center;
gap: 0px !important;
margin-left: auto;
gap: 10px !important;
}
/* 按钮样式 */
@ -1215,14 +1243,14 @@ const openDetail = async(dccode) => {
border-radius: 12px !important;
overflow: hidden !important;
border: 1px solid #e4e7ed !important;
min-height: 700px;
height: 650px;
}
.table-roundedDE {
border-radius: 12px !important;
overflow: hidden !important;
border: 1px solid #e4e7ed !important;
min-height: 650px;
height: 650px;
}
.table-header {

802
src/views/UserPermissions/invitedLook.vue

@ -0,0 +1,802 @@
<!-- @format -->
<template>
<div>
<div class="page-container">
<!-- 搜索区域 -->
<div class="search-container" @keyup.enter="search">
<div class="search-form">
<div class="search-group">
<div class="search-group1">
<div class="search-item">
<span class="form-label">账号</span>
<el-input v-model="searchForm.invitee" placeholder="请输入账号" clearable style="height: 36px; width: 140px" />
</div>
<div class="search-item">
<span class="form-label">姓名</span>
<el-input v-model="searchForm.username" placeholder="请输入姓名" clearable style="height: 36px; width: 180px" />
</div>
<div class="search-item">
<span class="form-label">手机号</span>
<el-input v-model="searchForm.phone" placeholder="请输入手机号" clearable style="height: 36px; width: 200px" />
</div>
<div class="search-item">
<span class="form-label">邮箱</span>
<el-input v-model="searchForm.email" placeholder="请输入邮箱" clearable style="height: 36px; width: 200px" />
</div>
</div>
<div class="search-group2">
<div class="search-item">
<span class="form-label">归属</span>
<el-input v-model="searchForm.inviter" placeholder="请输入归属账号" :disabled="permission != '2'" clearable style="height: 36px; width: 140px" />
</div>
<div class="search-item">
<span class="form-label">来源</span>
<el-select v-model="searchForm.origin_id" placeholder="请选择来源" clearable filterable style="height: 36px; width: 160px" :loading="isOriginLoading">
<el-option v-for="origin in originList" :key="origin.origin_id" :label="origin.origin_name" :value="origin.origin_id" />
</el-select>
</div>
<div class="search-item">
<span class="form-label">客户类型</span>
<el-select v-model="searchForm.user_role" placeholder="请选择客户类型" clearable style="height: 36px; width: 160px">
<el-option label="非网" value="2" />
<el-option label="会员" value="1" />
</el-select>
</div>
<div class="search-item">
<span class="form-label">地区</span>
<el-select v-model="searchForm.country" placeholder="请选择地区" clearable filterable style="height: 36px; width: 160px" :loading="isRegionLoading">
<el-option v-for="region in regionList" :key="region.ID" :label="region.Name" :value="region.ID" />
</el-select>
</div>
</div>
<div class="search-group3">
<div class="search-item">
<span class="form-label">注册时间</span>
<el-date-picker v-model="searchDate" type="datetimerange" :shortcuts="shortcuts" range-separator="" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" />
</div>
</div>
</div>
<div class="button-group">
<el-button type="primary" @click="search">搜索</el-button>
<el-button type="success" @click="exportExcel">导出Excel列表</el-button>
<el-button color="#626aef" @click="exportList">查看导出列表</el-button>
<el-button type="primary" @click="resetBn">重置</el-button>
</div>
</div>
</div>
<!-- 数据 -->
<el-table :data="tableData" style="width: 100%; margin-top: 20px" header-cell-class-name="table-header" class="table-rounded" :loading="tableLoading">
<el-table-column prop="id" label="序号" align="center" header-align="center" width="80">
<template #default="scope">
{{ (currentPage - 1) * pageSizeDM + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="invitee" label="账号" align="center" header-align="center" />
<el-table-column prop="username" label="姓名" align="center" header-align="center" />
<el-table-column prop="inviter" label="归属" align="center" header-align="center" />
<el-table-column prop="origin_name" label="来源" align="center" header-align="center" />
<el-table-column prop="email" label="邮箱" align="center" header-align="center" width="200">
<template #default="scope">
<el-tooltip effect="dark" :content="scope.row.email" placement="top">
<span class="ellipsis-text">{{ scope.row.email }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="dialing_code" label="手机区号" align="center" header-align="center" />
<el-table-column prop="phone" label="手机号" align="center" header-align="center" />
<el-table-column prop="country" label="地区" align="center" header-align="center" />
<el-table-column prop="regtime" label="注册时间" align="center" header-align="center" width="250" />
</el-table>
<!-- 分页组件 -->
<div class="demo-pagination-block">
<el-pagination
@size-change="handleSizeChangeDM"
@current-change="handleCurrentChangeDM"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSizeDM"
layout="total, sizes, prev, pager, next, jumper"
:total="datatotalDM"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
import { getInvitedListApi, exportInvitedApi, marketListApi, getOriginListApi } from "../../api/userPermissions";
import router from "../../router";
import { useRoute } from "vue-router";
import { usePermissionStore } from "../../store/permission";
const permissionStore = usePermissionStore();
// token
const token = localStorage.getItem("token");
//permission
const permission = ref("-1");
//
const route = useRoute();
// +
onMounted(async () => {
permission.value = permissionStore.permission;
fetchOriginList();
fetchRegionList();
getTableData();
});
const shortcuts = [
{
text: "最近7天",
value: () => {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 7);
return [start, end];
},
},
{
text: "最近1个月",
value: () => {
const end = new Date();
const start = new Date();
start.setMonth(start.getMonth() - 1);
return [start, end];
},
},
{
text: "最近3个月",
value: () => {
const end = new Date();
const start = new Date();
start.setMonth(start.getMonth() - 3);
return [start, end];
},
},
];
//
const regionList = ref([]);
const isRegionLoading = ref(false);
//
const fetchRegionList = async () => {
try {
isRegionLoading.value = true;
const data = await marketListApi({
token: token,
app_form: "en",
});
regionList.value = data.list;
} catch (error) {
console.error("获取地区列表失败:", error);
regionList.value = [];
} finally {
isRegionLoading.value = false;
}
};
//
const originList = ref([]);
const isOriginLoading = ref(false);
//
const fetchOriginList = async () => {
try {
isOriginLoading.value = true;
const data = await getOriginListApi({
token: token,
app_form: "en",
});
originList.value = data.list;
} catch (error) {
console.error("获取来源下拉框失败:", error);
originList.value = [];
} finally {
isOriginLoading.value = false;
}
};
//
const searchForm = reactive({
invitee: "",
username: "",
phone: "",
email: "",
inviter: "",
origin_id: "",
user_role: "",
country: "",
});
const searchDate = ref([]);
//
const sortOrder = ref(null);
//
const tableData = ref([]);
const tableLoading = ref(false);
const datatotalDM = ref(0);
//
const currentPage = ref(1);
const pageSizeDM = ref(10);
//
const getTableData = async () => {
try {
tableLoading.value = true;
const requestParams = {
token: token,
invitee: searchForm.invitee,
username: searchForm.username,
phone: searchForm.phone,
email: searchForm.email,
inviter: searchForm.inviter,
origin_id: searchForm.origin_id,
user_role: searchForm.user_role,
country: searchForm.country,
start_time: searchDate.value && searchDate.value[0] ? searchDate.value[0] : "",
end_time: searchDate.value && searchDate.value[1] ? searchDate.value[1] : "",
page: currentPage.value,
page_size: pageSizeDM.value,
};
const data = await getInvitedListApi(requestParams);
tableData.value = data.list;
datatotalDM.value = data.total;
} catch (error) {
console.error("获取表格数据失败:", error);
tableData.value = [];
datatotalDM.value = 0;
} finally {
tableLoading.value = false;
}
};
//
const handleSizeChangeDM = (val) => {
pageSizeDM.value = val;
getTableData();
console.log(`每页 ${val}`);
};
const handleCurrentChangeDM = (val) => {
currentPage.value = val;
getTableData();
console.log(`当前页: ${val}`);
};
//
const search = () => {
currentPage.value = 1;
getTableData();
};
//
const resetBn = () => {
searchForm.invitee = "";
searchForm.username = "";
searchForm.phone = "";
searchForm.email = "";
searchForm.inviter = "";
searchForm.origin_id = "";
searchForm.user_role = "";
searchForm.country = "";
searchDate.value = [];
currentPage.value = 1;
pageSizeDM.value = 10;
getTableData();
};
// Excel
const exportExcel = async () => {
const requestParams = {
token: token,
invitee: searchForm.invitee,
username: searchForm.username,
phone: searchForm.phone,
email: searchForm.email,
inviter: searchForm.inviter,
origin_id: searchForm.origin_id,
user_role: searchForm.user_role,
start_time: searchDate.value && searchDate.value[0] ? searchDate.value[0] : "",
end_time: searchDate.value && searchDate.value[1] ? searchDate.value[1] : "",
country: searchForm.country,
};
const data = await exportInvitedApi(requestParams);
if (data != "") {
ElMessage.success("已导出");
}
};
//
const exportList = () => {
router.push({
path: "/userPermissions/export",
});
};
//
const formatDate = (date) => {
if (!date) return "";
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}/${month}/${day}`;
};
//
const checkmodel = () => {
if (indicator_id.value.length === 0) {
ElMessage.error("请至少选择一个指标");
return false;
}
return true;
};
//
const checkTime = () => {
if (timeType.value === "expire") {
//
if (!expireTime.value) {
ElMessage.error("请选择到期时间");
return false;
}
} else if (timeType.value === "delay") {
//
if (!delayValue.value || !delayUnit.value) {
ElMessage.error("延期时间必须填写完整(数值+单位)");
return false;
}
const delayNum = Number(delayValue.value);
if (isNaN(delayNum) || delayNum < 0) {
ElMessage.error("延期时间不能为负数,请输入有效正数");
return false;
}
if (delayNum === 0) {
ElMessage.error("延期时间不能为0,请输入有效正数");
return false;
}
} else {
ElMessage.error("请设置权限时间");
return false;
}
return true;
};
//
const checkRemark = () => {
if (!remark.value.trim()) {
ElMessage.error("请输入备注");
return false;
}
return true;
};
</script>
<style scoped>
/* 父容器 */
.page-container {
position: relative;
min-height: 600px;
}
/* 搜索区域 */
.search-container {
display: flex;
height: 170px;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 10px;
align-self: stretch;
border-radius: 8px;
background: #fefaf9;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
padding: 0 15px;
margin-bottom: 20px;
}
/* 搜索表单 */
.search-form {
display: flex;
align-items: center;
width: 100%;
gap: 15px;
flex-wrap: nowrap;
}
.search-group {
}
.search-group1 {
display: flex;
align-items: center;
gap: 15px;
}
.search-group2 {
display: flex;
margin-top: 15px;
align-items: center;
gap: 15px;
}
.search-group3 {
display: flex;
margin-top: 15px;
align-items: center;
gap: 15px;
}
/* 单个搜索项 */
.search-item {
display: flex;
align-items: center;
gap: 6px;
}
/* 搜索标签文字 */
.form-label {
font-weight: 800 !important;
font-size: 15px;
text-align: left;
color: #333;
margin-top: 13px;
font-family: "SimHei", "Heiti SC", "Microsoft YaHei", sans-serif !important;
}
/* 按钮组 */
.button-group {
display: flex;
align-items: center;
gap: 0px !important;
margin-left: auto;
}
/* 按钮样式 */
.button-group .el-button {
padding: 6px 10px !important;
font-size: 14px !important;
height: 36px !important;
}
/* 表格样式 */
.table-rounded {
border-radius: 12px !important;
overflow: hidden !important;
border: 1px solid #e4e7ed !important;
height: 650px;
}
.table-roundedDE {
border-radius: 12px !important;
overflow: hidden !important;
border: 1px solid #e4e7ed !important;
min-height: 650px;
}
.table-header {
text-align: center !important;
font-weight: 800 !important;
font-size: 15px !important;
color: #333 !important;
background-color: #f8f9fa !important;
}
.el-table__cell {
border-right: none !important;
border-bottom: 1px solid #e4e7ed !important;
}
.el-table__header th.el-table__cell {
border-right: none !important;
border-bottom: 1px solid #e4e7ed !important;
}
.el-table__row:hover .el-table__cell {
background-color: #fafafa !important;
}
/* 分页组件样式 */
.demo-pagination-block {
display: flex;
width: 100%;
height: 44px;
padding: 0 16px;
align-items: center;
gap: 16px;
position: absolute;
margin-top: 10px;
border-radius: 0 0 3px 3px;
border-top: 1px solid #eaeaea;
background: #fefbfb;
box-sizing: border-box;
}
/* 添加/修改样式 */
.form-item {
margin-bottom: 24px;
padding-left: 20px;
padding-right: 20px;
text-align: left;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 12px;
align-items: flex-start;
}
.radio-item {
display: flex;
align-items: center;
}
.radio-item span {
margin-right: 8px;
}
.tip {
font-size: 12px;
color: #909399;
margin-top: 4px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 20px;
}
.inline-form-item {
display: flex;
align-items: flex-start;
gap: 12px;
}
.inline-form-item .form-label {
display: inline-block;
margin-bottom: 0;
width: 60px;
text-align: left;
padding-right: 12px;
}
.info-container {
display: flex;
align-items: center;
gap: 40px;
padding: 0 20px 18px;
color: #333;
font-size: 16px;
}
.info-item {
white-space: nowrap;
}
/* Tab容器 */
.tab-group {
display: flex;
gap: 12px;
justify-content: flex-start;
margin: 4px 0;
padding-left: 2px;
}
/* Tab按钮 */
.tab-btn {
width: 120px;
padding: 6px 0;
text-align: center;
border: 1px solid #ff0000;
border-radius: 4px;
background-color: #ffffff;
color: #ff0000;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
outline: none;
}
/* Tab选中样式 */
.tab-btn.active {
background-color: #ff0000;
color: #ffffff;
}
/* hover效果优化 */
.tab-btn:not(.active):hover {
background-color: #fff5f5;
}
/* 搜索区域(深度探索) */
.search-containerDE {
display: flex;
height: auto;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 12px;
align-self: stretch;
border-radius: 8px;
background: #fefaf9;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
padding: 15px;
margin-bottom: 20px;
}
/* 搜索表单(深度探索) */
.search-formDE {
display: flex;
align-items: center;
width: 100%;
gap: 15px;
flex-wrap: wrap;
row-gap: 8px;
}
/* 单个搜索项(深度探索) */
.search-itemDE {
display: flex;
align-items: center;
gap: 6px;
}
/* 搜索标签文字(深度探索) */
.form-labelDE {
font-weight: 800 !important;
font-size: 15px;
text-align: left;
color: #333;
margin-top: 0;
font-family: "SimHei", "Heiti SC", "Microsoft YaHei", sans-serif !important;
}
/* 按钮组(深度探索) */
.button-groupDE {
display: flex;
align-items: center;
gap: 10px !important;
}
/* 按钮样式(深度探索) */
.button-groupDE .el-button {
padding: 6px 10px !important;
font-size: 14px !important;
height: 36px !important;
}
/* 文本溢出省略样式(深度探索) */
.ellipsis-text {
display: inline-block;
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
/* 开通/编辑指标复选(深度探索) */
.indicator-checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
width: 100%;
}
.indicator-checkbox-item {
flex: 0 0 calc(20% - 5px);
box-sizing: border-box;
padding: 4px 0;
}
:deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
background-color: #ff0000 !important;
border-color: #ff0000 !important;
}
:deep(.el-checkbox__input.is-checked .el-checkbox__inner::after) {
border-color: #fff !important;
}
:deep(.el-checkbox__input:hover .el-checkbox__inner) {
border-color: #ff0000 !important;
}
:deep(.el-checkbox__input:focus .el-checkbox__inner) {
box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.2) !important;
}
:deep(.el-checkbox__label) {
color: #333 !important;
font-size: 14px !important;
}
/* 权限详情(深度探索) */
.permission-detail-dialog {
--el-dialog-padding-primary: 15px;
}
.detail-container {
background-color: #fff1f0;
border-radius: 6px;
padding: 15px;
}
.hlid-item {
margin-bottom: 25px;
}
.label {
color: #666;
font-weight: 500;
}
.value {
color: #333;
}
.expire-section {
margin-top: 10px;
}
.section-title {
font-size: 16px;
color: #333;
margin-bottom: 10px;
font-weight: bold;
}
.indicator-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.indicator-item {
padding: 6px 0;
display: flex;
align-items: center;
}
.indicator-name {
color: #666;
font-size: 14px;
}
.expire-time {
color: #333;
font-size: 14px;
margin-left: 8px;
}
.expired-tag {
background-color: #ffccd5;
color: #f56c6c;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
margin-left: 8px;
}
</style>

12
vite.config.js

@ -2,7 +2,13 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
base: '/deepChartBack/'
export default defineConfig(({mode})=>{
return {
plugins: [vue()],
base: '/deepChartBack/',
build: {
outDir: `dist_${mode}`,
}
}
})
Loading…
Cancel
Save