From 500c1ddb15b1e00aae3ecc5d8e067cb9bd4baf21 Mon Sep 17 00:00:00 2001 From: zhangtao <1176193409@qq.com> Date: Mon, 21 Nov 2022 09:32:31 +0800 Subject: [PATCH] add advertising components --- .env | 5 +- .env.development | 7 +- .env.localhost | 7 +- .env.production | 7 +- .env.test | 7 +- package.json | 1 + src/api/auth/index.js | 34 +++ src/api/setting/advertising.js | 48 ++++ src/api/user/index.js | 7 - src/components/DataTable/tools/Tags.vue | 11 +- src/components/TinymceEditor/index.vue | 7 +- src/layout/components/Header/index.vue | 10 +- src/layout/index.vue | 6 - src/router/guard/permission-guard.js | 60 +++-- src/setting/config.js | 4 +- src/store/modules/permission.js | 68 +++-- src/store/modules/user.js | 83 +++--- src/utils/dictionary.js | 17 ++ src/utils/http/interceptors.js | 48 ++-- src/utils/oidc/index.js | 91 +++++++ src/views/login/index.vue | 71 +++++- src/views/resources/monitor-manage/index.vue | 17 ++ .../components/AdvertisingModal.vue | 189 ++++++++++++++ src/views/setting/advertising/index.vue | 59 ++++- src/views/setting/advertising/tools/form.js | 57 +++++ src/views/setting/advertising/tools/search.js | 14 ++ src/views/setting/advertising/tools/table.js | 237 ++++++++++++++++++ .../statistics/statistical-analysis/index.vue | 20 +- 28 files changed, 1011 insertions(+), 181 deletions(-) create mode 100644 src/api/setting/advertising.js create mode 100644 src/utils/oidc/index.js create mode 100644 src/views/resources/monitor-manage/index.vue create mode 100644 src/views/setting/advertising/components/AdvertisingModal.vue create mode 100644 src/views/setting/advertising/tools/form.js create mode 100644 src/views/setting/advertising/tools/search.js create mode 100644 src/views/setting/advertising/tools/table.js diff --git a/.env b/.env index a5504aa..b6e694e 100644 --- a/.env +++ b/.env @@ -5,4 +5,7 @@ VITE_APP_TITLE = '智慧河长' VITE_PORT = 3000 -VITE_SERVER = "/" \ No newline at end of file +# VITE_SERVER = "/hhz/admin/api" +VITE_SERVER = "" + +VITE_PLATFORM = "tuoheng-hhz-admin" \ No newline at end of file diff --git a/.env.development b/.env.development index 67ee6a0..d9e321e 100644 --- a/.env.development +++ b/.env.development @@ -8,4 +8,9 @@ VITE_APP_USE_MOCK = false VITE_PROXY = [["/api","http://192.168.11.11:9011/api"]] # base api -VITE_APP_GLOB_BASE_API = '/api' \ No newline at end of file +VITE_APP_GLOB_BASE_API = '/api' + +VITE_AUTHORITY = 'http://192.168.11.11:8090' +VITE_CLIENT_ID = 'tuoheng-hhz-admin' +VITE_CLIENT_SECRET = 'qsPaU8a2YGFsZfIa7HoGSz==' +VITE_REDIRECT_URI = 'http://192.168.11.11:8086/login' \ No newline at end of file diff --git a/.env.localhost b/.env.localhost index b919d0f..bb90054 100644 --- a/.env.localhost +++ b/.env.localhost @@ -11,4 +11,9 @@ VITE_PROXY = [["/api-local","http://127.0.0.1:8002/api"],["/api-mock","http://12 VITE_APP_GLOB_BASE_API = '/api-local' # mock base api -VITE_APP_GLOB_BASE_API_MOCK = '/api-mock' \ No newline at end of file +VITE_APP_GLOB_BASE_API_MOCK = '/api-mock' + +VITE_AUTHORITY = 'http://192.168.11.11:8090' +VITE_CLIENT_ID = 'tuoheng-hhz-admin' +VITE_CLIENT_SECRET = 'qsPaU8a2YGFsZfIa7HoGSz==' +VITE_REDIRECT_URI = 'http://192.168.12.8:3000/login' \ No newline at end of file diff --git a/.env.production b/.env.production index 4ee2f3b..18f273d 100644 --- a/.env.production +++ b/.env.production @@ -8,4 +8,9 @@ VITE_APP_USE_MOCK = false VITE_PROXY = [["/api","http://127.0.0.1:8002/api"]] # base api -VITE_APP_GLOB_BASE_API = '/api' \ No newline at end of file +VITE_APP_GLOB_BASE_API = '/api' + +VITE_AUTHORITY = 'https://oidc.t-aaron.com' +VITE_CLIENT_ID = 'tuoheng-hhz-admin' +VITE_CLIENT_SECRET = 'qsPaU8a2YGFsZfIa7HoGSz==' +VITE_REDIRECT_URI = 'https://dsp-portal.t-aaron.com/login' \ No newline at end of file diff --git a/.env.test b/.env.test index 24a538c..c79cf84 100644 --- a/.env.test +++ b/.env.test @@ -8,4 +8,9 @@ VITE_APP_USE_MOCK = false VITE_PROXY = [["/api","http://192.168.11.241:9011/api"]] # base api -VITE_APP_GLOB_BASE_API = '/api' \ No newline at end of file +VITE_APP_GLOB_BASE_API = '/api' + +VITE_AUTHORITY = 'https://oidc.test.t-aaron.com' +VITE_CLIENT_ID = 'tuoheng-hhz-admin' +VITE_CLIENT_SECRET = 'qsPaU8a2YGFsZfIa7HoGSz==' +VITE_REDIRECT_URI = 'http://192.168.11.241:8086/login' \ No newline at end of file diff --git a/package.json b/package.json index 4a4a5bc..47add90 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "axios": "^0.26.1", "dayjs": "^1.11.2", "mockjs": "^1.1.0", + "oidc-client": "^1.11.5", "pinia": "^2.0.13", "pinia-plugin-persist": "^1.0.0", "tinymce": "^5.10.2", diff --git a/src/api/auth/index.js b/src/api/auth/index.js index f896d02..8ef3795 100644 --- a/src/api/auth/index.js +++ b/src/api/auth/index.js @@ -15,6 +15,40 @@ export const refreshToken = () => { }) } +/** + * @description: 获取menu和permission + * @param {String} roleId 角色Id + * @return {*} + */ +export const fetchPermission = (roleId) => { + return request({ + url: `/permission/getRolePermission/${roleId}`, + method: 'GET' + }) +} + +/** + * @description: 测试接口one + * @return {*} + */ +export const permissionOne = () => { + return request({ + url: 'permission/getOne', + method: 'GET' + }) +} + +/** + * @description: 测试接口two + * @return {*} + */ +export const permissionTwo = (id) => { + return request({ + url: 'permission/getTwo', + method: 'GET' + }) +} + export function getMenu() { return request({ url: '/index/getMenuList', diff --git a/src/api/setting/advertising.js b/src/api/setting/advertising.js new file mode 100644 index 0000000..c2d57df --- /dev/null +++ b/src/api/setting/advertising.js @@ -0,0 +1,48 @@ +import { defAxios as request } from '@/utils/http' + +/** + * @description: 分页查询广告列表 + * @return {*} + */ +export function fetchAdList(params) { + return request({ + url: '/ad/index', + method: 'get', + params + }) +} +/** + * @description: 创建广告 + * @return {*} + */ +export function advertisingCreate(data) { + return request({ + url: '/ad/add', + method: 'post', + data + }) +} + +/** + * @description: 更新广告信息 + * @return {*} + */ +export function advertisingUpdate(data) { + return request({ + url: `/ad/edit`, + method: 'put', + data + }) +} + +/** + * @description: 删除广告 + * @return {*} + */ +export function advertisingDelete(ids) { + return request({ + url: `/ad/delete/${ids}`, + method: 'delete' + }) +} + diff --git a/src/api/user/index.js b/src/api/user/index.js index a157826..d047b3a 100644 --- a/src/api/user/index.js +++ b/src/api/user/index.js @@ -8,13 +8,6 @@ export function getUsers(data = {}) { }) } -export function getUser() { - return request({ - url: '/index/getUserInfo', - method: 'get' - }) -} - export function saveUser(data = {}, id) { if (id) { return request({ diff --git a/src/components/DataTable/tools/Tags.vue b/src/components/DataTable/tools/Tags.vue index b1f0627..0d29f5a 100644 --- a/src/components/DataTable/tools/Tags.vue +++ b/src/components/DataTable/tools/Tags.vue @@ -76,9 +76,10 @@ export default defineComponent({ /* 获取tags的属性 */ const getProps = computed(() => { return { - ...unref(props.tags), closable: false, - bordered: props.tags?.bordered || false + bordered: false, + color: { color: 'transparent' }, + ...unref(props.tags) } }) return { @@ -93,7 +94,7 @@ export default defineComponent({ diff --git a/src/components/TinymceEditor/index.vue b/src/components/TinymceEditor/index.vue index aa2e28b..51d195a 100644 --- a/src/components/TinymceEditor/index.vue +++ b/src/components/TinymceEditor/index.vue @@ -141,7 +141,6 @@ export default { watch( () => props.modelValue, (value) => { - console.log(value) content.value = value } ) @@ -162,7 +161,7 @@ export default { diff --git a/src/layout/components/Header/index.vue b/src/layout/components/Header/index.vue index e940b5f..39226b7 100644 --- a/src/layout/components/Header/index.vue +++ b/src/layout/components/Header/index.vue @@ -24,6 +24,7 @@ import { EditOutlined, LogoutOutlined } from '@vicons/antd' import { renderIcon } from '@/utils' import { useUserStore } from '@/store/modules/user.js' import { useSettingStore } from '@/store/modules/setting.js' +import { signoutRedirect } from '@/utils/oidc/index.js' export default defineComponent({ name: 'LayoutHeader', setup() { @@ -57,14 +58,7 @@ export default defineComponent({ async function handleSelect(key) { switch (key) { case 'out': - await logOut() - } - } - - async function logOut() { - const res = await userStore.userLogout() - if (res.code === 0) { - router.replace('/login') + await signoutRedirect() } } diff --git a/src/layout/index.vue b/src/layout/index.vue index d7ebcab..22d24a4 100644 --- a/src/layout/index.vue +++ b/src/layout/index.vue @@ -32,15 +32,9 @@ import SideBar from './components/Sidebar/index.vue' import Tags from './components/Tags/index.vue' import { useSettingStore } from '@/store/modules/setting.js' import { computed } from 'vue' -import { useUserStore } from '@/store/modules/user.js' const settingStore = useSettingStore() const menuMode = computed(() => settingStore.getMenuMode) const tagsMenuSetting = computed(() => settingStore.getTagsMenuSetting) -const useUser = useUserStore() -function getUserNow() { - useUser.getUserInfo() -} -getUserNow() diff --git a/src/router/guard/permission-guard.js b/src/router/guard/permission-guard.js index dbb3d2c..ceb7db1 100644 --- a/src/router/guard/permission-guard.js +++ b/src/router/guard/permission-guard.js @@ -1,44 +1,50 @@ +/* + * @Author: whyafterme + * @Date: 2022-11-03 11:31:21 + * @LastEditTime: 2022-11-21 09:02:20 + * @LastEditors: whyafterme + * @Description: + * @FilePath: \new\src\router\guard\permission-guard.js + */ import { useUserStore } from '@/store/modules/user' import { usePermissionStore } from '@/store/modules/permission' import { NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes' -import { getToken } from '@/utils/token' +import { getUserInfo, signinRedirect, signoutRedirect } from '@/utils/oidc/index.js' -const WHITE_LIST = ['/login', '/redirect'] export function createPermissionGuard(router) { const userStore = useUserStore() const permissionStore = usePermissionStore() router.beforeEach(async(to, from, next) => { - // const token = getToken() - const token = true - if (token) { - if (to.path === '/login') { - next({ path: '/' }) + const oidcUser = await getUserInfo() + if (oidcUser) { + const hasRoutes = !!permissionStore.permissionRoutes.length + if (hasRoutes) { + next() } else { - const hasRoutes = !!permissionStore.permissionRoutes.length - if (hasRoutes) { - next() - } else { - try { - // await userStore.getUserInfo() - const routes = await permissionStore.generateRoutesMock() - routes.forEach((item) => { - router.addRoute(item) - }) - router.addRoute(NOT_FOUND_ROUTE) - router.addRoute(REDIRECT_ROUTE) - next({ ...to, replace: true }) - } catch (error) { - // removeToken() - // $message.error(error) - next({ path: '/login', query: { ...to.query, redirect: to.path }}) - } + try { + const { clientRoleList } = oidcUser.profile + const { VITE_CLIENT_ID } = import.meta.env + const { roleId } = clientRoleList.find((item) => { + return item.clientId === VITE_CLIENT_ID + }) + // await userStore.getUserInfo() + // const routes = await permissionStore.generateRoutes(roleId) + const routes = await permissionStore.generateRoutesMock() + routes.forEach((item) => { + router.addRoute(item) + }) + router.addRoute(NOT_FOUND_ROUTE) + router.addRoute(REDIRECT_ROUTE) + next({ ...to, replace: true }) + } catch (error) { + signinRedirect() } } } else { - if (WHITE_LIST.includes(to.path)) { + if (to.path === '/login') { next() } else { - next({ path: '/login', query: { ...to.query, redirect: to.path }}) + signinRedirect() } } }) diff --git a/src/setting/config.js b/src/setting/config.js index cd09f18..acaa734 100644 --- a/src/setting/config.js +++ b/src/setting/config.js @@ -17,8 +17,8 @@ const setting = { }, /* tags */ tagsMenuSetting: { - show: true, - fixed: true, + show: false, + fixed: false, background: '#f5f7f9' } } diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js index af9f9a7..92e6a4c 100644 --- a/src/store/modules/permission.js +++ b/src/store/modules/permission.js @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { asyncRoutes, basicRoutes } from '@/router/routes' -import { getMenu } from '@/api/auth/index' +import { fetchPermission } from '@/api/auth/index' import Layout from '@/layout/index.vue' import modules from '@/utils/module.js' @@ -44,55 +44,43 @@ function filterAsyncRoutes(routes = [], role) { return ret } -/** - * @description: - * @param {*} routes - * @return {*} - */ -function dataArrayToRoutes(routes) { +function dealRoutes(routes) { const res = [] routes.forEach(item => { const tmp = { ...item } - // // 如果有component配置 - // if (tmp.component) { - // // Layout引入 - // if (tmp.component === 'Layout') { - // tmp.component = Layout - // } else { - // const sub_view = tmp.component.replace(/^\/*/g, '') - // const component = `../${sub_view}.vue` - // tmp.component = modules[component] - // } - // if (tmp.children) { - // tmp.children = dataArrayToRoutes(tmp.children) - // } - // } - // 如果pid为0 - if (tmp.pid === 0) { - // Layout引入 + if (tmp.parentId === 0) { tmp.component = Layout if (tmp.children) { - tmp.children = dataArrayToRoutes(tmp.children) + tmp.redirect = tmp.path === '/' ? `/${tmp.children[0].path}` : `${tmp.path}/${tmp.children[0].path}` + tmp.children = dealRoutes(tmp.children) } } else { const sub_view = tmp.component.replace(/^\/*/g, '') const component = `../${sub_view}.vue` tmp.component = modules[component] } - tmp.name = tmp.title + tmp.title = tmp.name tmp.meta = { - ...tmp.meta, - title: tmp.meta.title || tmp.title + ...tmp?.meta, + title: tmp.name } res.push(tmp) }) return res } +function dealPermissions(permissionsList) { + const res = permissionsList.map((tmp) => { + return tmp.code + }) + return res +} + export const usePermissionStore = defineStore('permission', { state() { return { - accessRoutes: [] + accessRoutes: [], + accessPermissions: [] } }, getters: { @@ -101,23 +89,31 @@ export const usePermissionStore = defineStore('permission', { }, permissionRoutes() { return this.accessRoutes + }, + validatePermission() { + return this.accessPermissions } }, actions: { generateRoutesMock(role = []) { const accessRoutes = filterAsyncRoutes(asyncRoutes, role) this.accessRoutes = accessRoutes - console.log(accessRoutes) return accessRoutes }, - async generateRoutes() { + async generateRoutes(roleId) { try { - const res = await getMenu() + const res = await fetchPermission(roleId) + // const res = await fetchPermission(3) if (res.code === 0) { - const result = dataArrayToRoutes(res.data) - console.log(result) - this.accessRoutes = result - return Promise.resolve(result) + const { opMenusList, permissionsList } = res.data + const menus = dealRoutes(opMenusList) + const permissions = dealPermissions(permissionsList) + this.accessRoutes = menus + this.accessPermissions = permissions + return Promise.resolve(menus) + // const accessRoutes = filterAsyncRoutes(asyncRoutes) + // this.accessRoutes = accessRoutes + // return Promise.resolve(accessRoutes) } else { return Promise.reject(res.message) } diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 87841d5..a84f5d8 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,71 +1,56 @@ import { defineStore } from 'pinia' -import { userLogin, loginOut } from '@/api/login' import { getUser } from '@/api/login/index.js' -import { setToken, removeToken } from '@/utils/token' - -import { useTagsMenuStore } from './tagsMenu.js' -import { usePermissionStore } from './permission.js' export const useUserStore = defineStore('user', { - persist: true, + persist: { + enabled: true + }, state() { return { - userInfo: {} + userInfo: { + hasLogin: false + } } }, getters: { - userInfoMsg() { - return this.userInfo + // userId() { + // return this.userInfo?.id + // }, + userName() { + return this.userInfo?.userName + }, + realname() { + return this.userInfo?.realname + }, + avatar() { + return this.userInfo?.avatar + }, + role() { + return this.userInfo?.role || [] + }, + authority() { + return this.userInfo.authority + }, + hasLogin() { + return this.userInfo.hasLogin } }, actions: { - /* 登录 */ - async getLoginToken(form) { + async getUserInfos() { try { - const res = await userLogin(form) + const res = await getUser() if (res.code === 0) { - /* 设置token */ - setToken(res.data.access_token) - this.getUserInfo() - return Promise.resolve(res) + this.setUserInfo({ hasLogin: true, ...res.data }) + return Promise.resolve(res.data.type) } else { - return Promise.reject(res) + this.setUserInfo() } } catch (error) { - return Promise.reject(error) + console.error(error) + this.setUserInfo() } }, - /* 获取用户信息 */ - async getUserInfo() { - const res = await getUser() - if (res.code === 0) { - this.setUserInfo(res.data) - } - }, - async userLogout() { - try { - const res = await loginOut() - if (res.code === 0) { - removeToken() - this.reset() - return Promise.resolve(res) - } else { - return Promise.reject(res) - } - } catch (error) { - return Promise.reject(error) - } - }, - - reset() { - this.$reset() - const tagsMenuStore = useTagsMenuStore() - const permissionStore = usePermissionStore() - tagsMenuStore.$reset() - permissionStore.$reset() - }, - - setUserInfo(userInfo = {}) { + setUserInfo(userInfo = { hasLogin: false }) { this.userInfo = { ...this.userInfo, ...userInfo } } } diff --git a/src/utils/dictionary.js b/src/utils/dictionary.js index fffa5bd..fc848cd 100644 --- a/src/utils/dictionary.js +++ b/src/utils/dictionary.js @@ -28,3 +28,20 @@ export const MENU_STATUS = [ { label: '在用', value: 1 }, { label: '停用', value: 2 } ] + +export const ADVERTISING_TYPE = [ + { label: '图片', value: 1, color: { color: '#f6ffed', textColor: '#389e0d', borderColor: '#b7eb8f' }}, + { label: '文字', value: 2, color: { color: '#e6f7ff', textColor: '#096dd9', borderColor: '#91d5ff' }}, + { label: '视频', value: 2, color: { color: '#fff1f0', textColor: '#cf1322', borderColor: '#ffa39e' }}, + { label: '推荐', value: 2, color: { color: '#fff7e6', textColor: '#d46b08', borderColor: '#ffd591' }} +] + +export const ADVERTISING_PLATFORM = [ + { label: '微信小程序', value: 1, color: { color: '#e6f7ff', textColor: '#096dd9', borderColor: '#91d5ff' }}, + { label: '其他', value: 2, color: { color: '#f6ffed', textColor: '#389e0d', borderColor: '#b7eb8f' }} +] + +export const ADVERTISING_STATUS = [ + { label: '正常', value: 1, color: { color: '#e6f7ff', textColor: '#096dd9', borderColor: '#91d5ff' }}, + { label: '停用', value: 2, color: { color: '#fff1f0', textColor: '#cf1322', borderColor: '#ffa39e' }} +] diff --git a/src/utils/http/interceptors.js b/src/utils/http/interceptors.js index 57fa90c..d9369ad 100644 --- a/src/utils/http/interceptors.js +++ b/src/utils/http/interceptors.js @@ -1,6 +1,5 @@ -import { router } from '@/router' -import { getToken, removeToken } from '@/utils/token' import { isWithoutToken } from './help' +import { getUserInfo, signoutRedirect } from '@/utils/oidc/index.js' export function setupInterceptor(service) { service.interceptors.request.use( @@ -12,22 +11,20 @@ export function setupInterceptor(service) { // 处理不需要token的请求 if (isWithoutToken(config)) { return config + } else { + const userInfo = await getUserInfo() + if (userInfo) { + const { token_type, access_token } = userInfo + // config.headers.Authorization = `${token_type} ${access_token}` + config.headers.Authorization = '70aa58b4-dda7-446d-8cbf-8e6d6ab89a02' + const { VITE_CLIENT_ID } = import.meta.env + config.headers['Client-Id'] = VITE_CLIENT_ID + return config + } else { + signoutRedirect() + return Promise.reject({ response: { status: 401, message: '未登录' }}) + } } - const token = getToken() - if (token) { - config.headers.Authorization = token - return config - } - /** - * * 未登录或者token过期的情况下 - * * 跳转登录页重新登录,携带当前路由及参数,登录成功会回到原来的页面 - */ - const { currentRoute } = router - router.replace({ - path: '/login', - query: { ...currentRoute.query, redirect: currentRoute.path } - }) - return Promise.reject({ code: '-1', message: '未登录' }) }, (error) => Promise.reject(error) ) @@ -36,7 +33,6 @@ export function setupInterceptor(service) { (response) => { const { method } = response?.config const { code } = response?.data - const { currentRoute } = router switch (code) { case 0: if (method !== 'get') { @@ -48,11 +44,7 @@ export function setupInterceptor(service) { break case 401: // 未登录(可能是token过期或者无效了) - removeToken() - router.replace({ - path: '/login', - query: { ...currentRoute.query, redirect: currentRoute.path } - }) + signoutRedirect() break default: break @@ -60,7 +52,15 @@ export function setupInterceptor(service) { return response?.data }, (error) => { - return Promise.reject(error) + const { status } = error.response + if (status === 401) { + signoutRedirect() + } else if (status === 403) { + $message.error('暂无权限访问,请联系管理员') + return Promise.reject(error) + } else { + return Promise.reject(error) + } } ) } diff --git a/src/utils/oidc/index.js b/src/utils/oidc/index.js new file mode 100644 index 0000000..f631ba2 --- /dev/null +++ b/src/utils/oidc/index.js @@ -0,0 +1,91 @@ +import { UserManager } from 'oidc-client' +import { createLocalStorage } from '../cache' +let oidcManager = null + +export const initServe = () => { + if (oidcManager) return oidcManager + const { VITE_AUTHORITY, VITE_CLIENT_ID, VITE_CLIENT_SECRET, VITE_REDIRECT_URI } = import.meta.env + oidcManager = new UserManager({ + /* 认证服务器 */ + authority: VITE_AUTHORITY, + /* 客户端id */ + client_id: VITE_CLIENT_ID, + client_secret: VITE_CLIENT_SECRET, + /* 回调客户端页面 */ + redirect_uri: VITE_REDIRECT_URI, + post_logout_redirect_uri: VITE_REDIRECT_URI, + response_type: 'code', + /* 授权范围 */ + scope: 'openid profile', + automaticSilentRenew: true, + revokeAccessTokenOnSignout: true, + metadata: { + issuer: `${VITE_AUTHORITY}`, + authorization_endpoint: `${VITE_AUTHORITY}/oauth2/authorize`, + token_endpoint: `${VITE_AUTHORITY}/oauth2/token`, + userinfo_endpoint: `${VITE_AUTHORITY}/userinfo`, + end_session_endpoint: `${VITE_AUTHORITY}/toLogout`, + revocation_endpoint: `${VITE_AUTHORITY}/oauth2/revoke` + } + }) + return oidcManager +} + +export const getUserInfo = async() => { + oidcManager = initServe() + return await oidcManager.getUser() +} +/* 登录 */ +export const signinRedirect = async() => { + oidcManager = initServe() + oidcManager.signinRedirect({}) + .then(res => { + setPath(window.location.pathname) + console.log('signed in', res) + }).catch(err => { + console.err(err) + }) +} +/* 登录回调 */ +export const signinRedirectCallback = async() => { + const userInfo = await getUserInfo() + if (!userInfo) { + oidcManager = initServe() + return await oidcManager.signinRedirectCallback() + } +} +/* 退出 */ +export const signoutRedirect = () => { + oidcManager = initServe() + oidcManager.signoutRedirect({}) + .then(function(res) { + setPath(window.location.pathname) + console.log('signed out', res) + }).catch(function(err) { + console.log(err) + }) +} +/* 退出回调 */ +export const signoutRedirectCallback = async() => { + const userInfo = await getUserInfo() + if (!userInfo) { + oidcManager = initServe() + return await oidcManager.signoutRedirectCallback() + } +} + +const PATH_CODE = 'redirect_path' +const DURATION = 24 * 60 * 60 +export const isPath = createLocalStorage() +/* 获取Path */ +export function getPath() { + return isPath.get(PATH_CODE) +} +/* 设置Path */ +export function setPath(Path, duration = DURATION) { + isPath.set(PATH_CODE, Path, duration) +} +/* 移出Path */ +export function removePath() { + isPath.remove(PATH_CODE) +} diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 620b5a0..e84dc17 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -1,17 +1,78 @@ diff --git a/src/views/resources/monitor-manage/index.vue b/src/views/resources/monitor-manage/index.vue new file mode 100644 index 0000000..dd011de --- /dev/null +++ b/src/views/resources/monitor-manage/index.vue @@ -0,0 +1,17 @@ + + + + diff --git a/src/views/setting/advertising/components/AdvertisingModal.vue b/src/views/setting/advertising/components/AdvertisingModal.vue new file mode 100644 index 0000000..ccf1e0c --- /dev/null +++ b/src/views/setting/advertising/components/AdvertisingModal.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/src/views/setting/advertising/index.vue b/src/views/setting/advertising/index.vue index 555d821..d5ca2ed 100644 --- a/src/views/setting/advertising/index.vue +++ b/src/views/setting/advertising/index.vue @@ -1,17 +1,72 @@ diff --git a/src/views/setting/advertising/tools/form.js b/src/views/setting/advertising/tools/form.js new file mode 100644 index 0000000..7968a1f --- /dev/null +++ b/src/views/setting/advertising/tools/form.js @@ -0,0 +1,57 @@ +import { ref, reactive } from 'vue' +import { ADVERTISING_TYPE, ADVERTISING_PLATFORM, ADVERTISING_STATUS } from '@/utils/dictionary.js' + +export const form = reactive({ + advertisingForm: { + cover: null, + title: null, + description: null, + type: 1, + platform: 1, + url: null, + status: 1, + width: null, + height: null, + startTime: null, + endTime: null, + sort: null, + content: null + }, + advertisingRules: { + title: [{ required: true, message: '请输入广告标题', trigger: 'blur' }], + sort: [{ required: true, type: 'number', message: '请输入排序号', trigger: 'blur' }] + }, + formItem: [ + { type: 'oss', refIndex: 0, key: 'imageStatus', file: 'cover', label: '广告图片', props: { maxlength: '20', placeholder: '请输入部门编号', clearable: true }}, + + { type: 'input', key: 'title', label: '广告标题', props: { maxlength: '20', placeholder: '请输入广告标题', clearable: true }}, + { type: 'input', key: 'description', label: '广告描述', props: { maxlength: '20', placeholder: '请输入广告描述', clearable: true }}, + + { type: 'select', key: 'type', label: '广告类型', props: { options: ADVERTISING_TYPE, placeholder: '请选择广告类型' }}, + { type: 'select', key: 'platform', label: '投放平台', props: { options: ADVERTISING_PLATFORM, placeholder: '请选择投放平台' }}, + + { type: 'input', key: 'url', label: '广告URL', props: { maxlength: '20', placeholder: '请输入广告URL', clearable: true }}, + { type: 'radio', key: 'status', label: '广告状态', options: ADVERTISING_STATUS, mode: ['config'] }, + + { type: 'number', key: 'width', label: '广告宽度', props: { min: 0, placeholder: '请输入广告宽度', showButton: false, clearable: true }}, + { type: 'number', key: 'height', label: '广告高度', props: { min: 0, placeholder: '请输入广告高度', showButton: false, clearable: true }}, + + { type: 'date', key: 'startTime', label: '开始时间', props: { + type: 'datetime', + valueFormat: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss', + actions: ['clear', 'confirm'], + timePickerProps: { actions: ['confirm'] } + }}, + { type: 'date', key: 'endTime', label: '结束时间', props: { + type: 'datetime', + valueFormat: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss', + actions: ['clear', 'confirm'], + timePickerProps: { actions: ['confirm'] } + }}, + + { type: 'number', key: 'sort', label: '排序号', props: { min: 0, placeholder: '请输入排序号', showButton: false, clearable: true }}, + + { type: 'editor', key: 'content', label: '广告内容', props: { height: 300 }} + ] +}) + diff --git a/src/views/setting/advertising/tools/search.js b/src/views/setting/advertising/tools/search.js new file mode 100644 index 0000000..3531025 --- /dev/null +++ b/src/views/setting/advertising/tools/search.js @@ -0,0 +1,14 @@ +import { reactive } from 'vue' + +const data = reactive([ + { + label: '广告名称', + key: 'title', + props: { + placeholder: '请输入广告名称' + } + } +]) + +export default data + diff --git a/src/views/setting/advertising/tools/table.js b/src/views/setting/advertising/tools/table.js new file mode 100644 index 0000000..66cf4a6 --- /dev/null +++ b/src/views/setting/advertising/tools/table.js @@ -0,0 +1,237 @@ +import { h, ref, reactive } from 'vue' +import TableImage from '@/components/DataTable/tools/Image.vue' +import TableTags from '@/components/DataTable/tools/Tags.vue' +import TableAction from '@/components/DataTable/tools/Action.vue' +import { ADVERTISING_TYPE, ADVERTISING_PLATFORM, ADVERTISING_STATUS } from '@/utils/dictionary.js' +import { advertisingDelete } from '@/api/setting/advertising.js' + +/* 注册table */ +const tableRef = ref() +const searchParams = ref() + +function handleSearch(params) { + searchParams.value = { ...params } + tableRef.value.reFetch({ searchParams }) +} + +/** + * @description: 获取数据及操作 + * @param {*} row 单行数据 + * @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 + * @return {*} + */ +function getRowData(row, type) { + data.rowData = type === 'create' ? { pid: row.id } : row + data.modalType = type + data.modalShow = true +} + +// 删除方法 +function deleteData(id) { + advertisingDelete(id) + .then((res) => { + if (res.code === 0) { + handleSearch() + } + }) + .catch((e) => { + console.log(e) + }) +} + +const data = reactive({ + tableRef, + searchParams, + rowData: {}, + modalType: 'create', + modalShow: false, + handleSearch, + + columns: [ + { + type: 'selection' + }, + { + title: '编号', + key: 'key', + render: (_, index) => { + return `${index + 1}` + }, + align: 'center', + width: 50 + }, + { + title: '广告名称', + key: 'title', + align: 'center', + width: 200 + }, + { + title: '广告封面', + key: 'cover', + render(row) { + return h(TableImage, { + images: { + width: 36, + height: 36, + src: row.cover + } + }) + }, + align: 'center', + width: 100 + }, + { + title: '广告类型', + key: 'type', + align: 'center', + width: 100, + render(row) { + return h(TableTags, { + data: row.type, + filters: ADVERTISING_TYPE, + tags: { + bordered: true + } + }) + } + }, + { + title: '投放平台', + key: 'platform', + align: 'center', + width: 100, + render(row) { + return h(TableTags, { + data: row.platform, + filters: ADVERTISING_PLATFORM, + tags: { + bordered: true + } + }) + } + }, + { + title: '广告描述', + key: 'description', + align: 'center', + ellipsis: { + tooltip: true + }, + width: 250 + }, + { + title: '广告地址', + key: 'url', + align: 'center', + ellipsis: { + tooltip: true + }, + width: 200 + }, + { + title: '广告尺寸', + key: 'size', + align: 'center', + render(row) { + return h(TableTags, { + data: `${row.width} x ${row.height}` + }) + }, + width: 100 + }, + { + title: '开始时间', + key: 'startTime', + align: 'center', + width: 200 + }, + { + title: '结束时间', + key: 'endTime', + align: 'center', + width: 200 + }, + { + title: '浏览量', + key: 'viewNum', + align: 'center', + width: 100 + }, + { + title: '状态', + key: 'status', + align: 'center', + width: 50, + render(row) { + return h(TableTags, { + data: row.status, + filters: ADVERTISING_STATUS, + tags: { + bordered: true + } + }) + } + }, + { + title: '排序', + key: 'sort', + align: 'center', + width: 50 + }, + { + title: '创建时间', + key: 'createTime', + align: 'center', + width: 200 + }, + { + title: '操作', + align: 'center', + width: 150, + fixed: 'right', + render(row) { + return h(TableAction, { + actions: [ + { + label: '添加', + type: 'button', + props: { + type: 'primary', + text: true, + onClick: getRowData.bind(null, row, 'create') + }, + auth: 'basic_list' + }, + { + label: '修改', + type: 'button', + props: { + type: 'primary', + text: true, + onClick: getRowData.bind(null, row, 'update') + }, + auth: 'basic_list' + }, + { + label: '删除', + type: 'popconfirm', + auth: 'basic_list', + tip: '确定删除这条数据吗?', + props: { + onPositiveClick: deleteData.bind(null, [row.id]) + }, + ButtonProps: { + text: true, + type: 'primary' + } + } + ], + align: 'center' + }) + } + } + ] +}) + +export default data diff --git a/src/views/statistics/statistical-analysis/index.vue b/src/views/statistics/statistical-analysis/index.vue index 0a522f7..3b691fd 100644 --- a/src/views/statistics/statistical-analysis/index.vue +++ b/src/views/statistics/statistical-analysis/index.vue @@ -1,14 +1,32 @@