diff --git a/src/api/scheduleOverview/index.js b/src/api/scheduleOverview/index.js new file mode 100644 index 0000000..c8ddcb1 --- /dev/null +++ b/src/api/scheduleOverview/index.js @@ -0,0 +1,81 @@ +import request from '@/utils/request' + +export function getOverview(params) { + return request({ + method: 'get', + url: '/api/admin/schedule-overview/overview', + params + }) +} + +export function getSystemList(params) { + return request({ + method: 'get', + url: '/api/admin/schedule-overview/systems/index', + params + }) +} + +export function saveSystem(data) { + return request({ + method: 'post', + url: '/api/admin/schedule-overview/systems/save', + data + }) +} + +export function destroySystem(params) { + return request({ + method: 'get', + url: '/api/admin/schedule-overview/systems/destroy', + params + }) +} + +export function getCourseList(params) { + return request({ + method: 'get', + url: '/api/admin/schedule-overview/courses/index', + params + }) +} + +export function saveCourse(data) { + return request({ + method: 'post', + url: '/api/admin/schedule-overview/courses/save', + data + }) +} + +export function destroyCourse(params) { + return request({ + method: 'get', + url: '/api/admin/schedule-overview/courses/destroy', + params + }) +} + +export function getScheduleList(params) { + return request({ + method: 'get', + url: '/api/admin/schedule-overview/schedules/index', + params + }) +} + +export function saveSchedule(data) { + return request({ + method: 'post', + url: '/api/admin/schedule-overview/schedules/save', + data + }) +} + +export function destroySchedule(params) { + return request({ + method: 'get', + url: '/api/admin/schedule-overview/schedules/destroy', + params + }) +} diff --git a/src/router/index.js b/src/router/index.js index 29e4211..d00352a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -31,55 +31,68 @@ import Layout from '@/layout' * a base page that does not have permission requirements * all roles can be accessed */ -export const constantRoutes = [{ - path: '/login', - component: () => import('@/views/login/index'), - hidden: true - }, - - { - path: '/404', - component: () => import('@/views/404'), - hidden: true - }, - { - path: '/test', - component: () => import('@/views/component/test.vue'), - hidden: true - }, - { - path: '/info', - component: Layout, - children: [{ - path: 'password', - component: () => import('@/views/system/password'), - name: '密码修改', - meta: { - title: '密码修改' - } - }], - hidden: true - }, - - { - path: '/', - component: Layout, - redirect: '/dashboard', - children: [{ - path: 'dashboard', - name: '课程发布管理', - component: () => import('@/views/course/index'), - meta: { - title: '课程发布管理', - icon: 'dashboard' - } +export const constantRoutes = [ + { + path: '/login', + component: () => import('@/views/login/index'), + hidden: true + }, + { + path: '/404', + component: () => import('@/views/404'), + hidden: true + }, + { + path: '/test', + component: () => import('@/views/component/test.vue'), + hidden: true + }, + { + path: '/info', + component: Layout, + children: [{ + path: 'password', + component: () => import('@/views/system/password'), + name: '密码修改', + meta: { + title: '密码修改' + } }], - hidden: true - }, - { - path: '/course/txl', - component: Layout, - redirect: '/dashboard', + hidden: true + }, + { + path: '/', + component: Layout, + redirect: '/dashboard', + children: [{ + path: 'dashboard', + name: '课程发布管理', + component: () => import('@/views/course/index'), + meta: { + title: '课程发布管理', + icon: 'dashboard' + } + }], + hidden: true + }, + { + path: '/schedule-overview', + component: Layout, + redirect: '/schedule-overview/index', + children: [{ + path: 'index', + name: '课程排期总览', + component: () => import('@/views/scheduleOverview/index'), + meta: { + title: '课程排期总览', + icon: 'table' + } + }] + }, + { + path: '/course/txl', + component: Layout, + redirect: '/dashboard', children: [{ path: '/course/txl', name: '通讯录', @@ -140,18 +153,13 @@ export const constantRoutes = [{ }], hidden: true } -] - - - -/** - * asyncRoutes - * the routes that need to be dynamically loaded based on user roles - */ -export const asyncRoutes = [ - - - // 404 page must be placed at the end !!! +] +/** + * asyncRoutes + * the routes that need to be dynamically loaded based on user roles + */ +export const asyncRoutes = [ + // 404 page must be placed at the end !!! { path: '*', redirect: '/404', diff --git a/src/views/scheduleOverview/README.md b/src/views/scheduleOverview/README.md new file mode 100644 index 0000000..eac2c6d --- /dev/null +++ b/src/views/scheduleOverview/README.md @@ -0,0 +1 @@ +课程排期总览静态页面用于根据设计参考图快速复原前台展示结构,当前版本仅包含静态示意数据与布局样式。 diff --git a/src/views/scheduleOverview/index.vue b/src/views/scheduleOverview/index.vue new file mode 100644 index 0000000..7348ec9 --- /dev/null +++ b/src/views/scheduleOverview/index.vue @@ -0,0 +1,1542 @@ + + + + + diff --git a/src/views/scheduleOverview/mockService.js b/src/views/scheduleOverview/mockService.js new file mode 100644 index 0000000..ac42623 --- /dev/null +++ b/src/views/scheduleOverview/mockService.js @@ -0,0 +1,235 @@ +const STORAGE_KEY = 'schedule-overview-mock-store' + +const getCurrentYear = () => String(new Date().getFullYear()) + +const clone = (data) => JSON.parse(JSON.stringify(data)) + +const createInitialState = () => { + const year = getCurrentYear() + + return { + systems: [ + { id: 1, name: '0-1', sort: 1 }, + { id: 2, name: '0-10', sort: 2 }, + { id: 3, name: '10-100', sort: 3 }, + { id: 4, name: '产业链协同组织', sort: 4 }, + { id: 5, name: '科创人才服务', sort: 5 }, + { id: 6, name: '锚定资金舍客万人成员', sort: 6 } + ], + courses: [ + { id: 1, system_id: 1, name: '领跑营', sort: 1 }, + { id: 2, system_id: 2, name: '高研班', sort: 1 }, + { id: 3, system_id: 3, name: '专题班', sort: 1 }, + { id: 4, system_id: 4, name: '产业加速营', sort: 1 }, + { id: 5, system_id: 4, name: '第二课堂', sort: 2 }, + { id: 6, system_id: 5, name: '人才研修', sort: 1 }, + { id: 7, system_id: 5, name: '科技大讲堂', sort: 2 }, + { id: 8, system_id: 6, name: '万人培训项目', sort: 1 } + ], + schedules: [ + { id: 1, year, system_id: 1, course_id: 1, month: 5, title: '第二期领跑营', owner: '张三', location: '苏州', count_text: '招募 42人' }, + { id: 2, year, system_id: 1, course_id: 1, month: 7, title: '第三期领跑营', owner: '张三', location: '苏州', count_text: '招募 38人' }, + { id: 3, year, system_id: 1, course_id: 1, month: 9, title: '第四期领跑营', owner: '张三', location: '苏州', count_text: '招募 41人' }, + { id: 4, year, system_id: 1, course_id: 1, month: 10, title: '第五期领跑营', owner: '李四', location: '苏州', count_text: '招募 35人' }, + { id: 5, year, system_id: 2, course_id: 2, month: 3, title: '第七期高研班', owner: '李四', location: '苏州', count_text: '招募 31人' }, + { id: 6, year, system_id: 2, course_id: 2, month: 5, title: '第八期高研班', owner: '李四', location: '上海', count_text: '招募 44人' }, + { id: 7, year, system_id: 2, course_id: 2, month: 7, title: '第九期高研班', owner: '王五', location: '苏州', count_text: '招募 40人' }, + { id: 8, year, system_id: 2, course_id: 2, month: 8, title: '第十期高研班', owner: '王五', location: '深圳', count_text: '招募 36人' }, + { id: 9, year, system_id: 3, course_id: 3, month: 6, title: '研学营', owner: '王五', location: '苏州', count_text: '招募 45人' }, + { id: 10, year, system_id: 4, course_id: 4, month: 4, title: 'AI产业营', owner: '赵六', location: '苏州', count_text: '招募 36人' }, + { id: 11, year, system_id: 4, course_id: 4, month: 5, title: 'OCP加速营', owner: '赵六', location: '常州', count_text: '招募 34人' }, + { id: 12, year, system_id: 4, course_id: 4, month: 6, title: '并购闭门营', owner: '赵六', location: '北京', count_text: '招募 27人' }, + { id: 13, year, system_id: 4, course_id: 4, month: 8, title: '数字化闭门营', owner: '孙七', location: '成都', count_text: '招募 30人' }, + { id: 14, year, system_id: 4, course_id: 4, month: 9, title: '智造闭门营', owner: '孙七', location: '苏州', count_text: '招募 30人' }, + { id: 15, year, system_id: 5, course_id: 6, month: 7, title: '领军营先导班', owner: '周八', location: '苏州', count_text: '招募 32人' }, + { id: 16, year, system_id: 5, course_id: 6, month: 9, title: '领军营专题班', owner: '周八', location: '苏州', count_text: '招募 36人' }, + { id: 17, year, system_id: 6, course_id: 8, month: 4, title: '第一期万人营', owner: '张三', location: '苏州', count_text: '招募 75人' }, + { id: 18, year, system_id: 6, course_id: 8, month: 5, title: '第二期万人营', owner: '张三', location: '扬州', count_text: '招募 102人' }, + { id: 19, year, system_id: 6, course_id: 8, month: 6, title: '第三期万人营', owner: '张三', location: '苏州', count_text: '招募 104人' }, + { id: 20, year, system_id: 6, course_id: 8, month: 7, title: '第四期万人营', owner: '张三', location: '苏州', count_text: '招募 78人' }, + { id: 21, year, system_id: 6, course_id: 8, month: 8, title: '第五期万人营', owner: '张三', location: '苏州', count_text: '招募 83人' }, + { id: 22, year, system_id: 6, course_id: 8, month: 9, title: '第六期万人营', owner: '张三', location: '苏州', count_text: '招募 81人' }, + { id: 23, year, system_id: 6, course_id: 8, month: 10, title: '第七期万人营', owner: '张三', location: '苏州', count_text: '招募 74人' }, + { id: 24, year, system_id: 6, course_id: 8, month: 11, title: '第八期万人营', owner: '张三', location: '苏州', count_text: '招募 66人' }, + { id: 25, year, system_id: 6, course_id: 8, month: 12, title: '第九期万人营', owner: '张三', location: '苏州', count_text: '招募 58人' } + ] + } +} + +const getStorage = () => { + if (typeof window === 'undefined' || !window.localStorage) { + return null + } + return window.localStorage +} + +const loadState = () => { + const storage = getStorage() + if (!storage) { + return createInitialState() + } + + const cache = storage.getItem(STORAGE_KEY) + if (!cache) { + const initialState = createInitialState() + storage.setItem(STORAGE_KEY, JSON.stringify(initialState)) + return initialState + } + + try { + return JSON.parse(cache) + } catch (error) { + const initialState = createInitialState() + storage.setItem(STORAGE_KEY, JSON.stringify(initialState)) + return initialState + } +} + +const store = loadState() + +const persist = () => { + const storage = getStorage() + if (storage) { + storage.setItem(STORAGE_KEY, JSON.stringify(store)) + } +} + +const nextId = (list) => list.reduce((max, item) => Math.max(max, Number(item.id) || 0), 0) + 1 + +const sortByName = (list) => clone(list).sort((a, b) => { + const sortDiff = Number(a.sort || 0) - Number(b.sort || 0) + if (sortDiff !== 0) { + return sortDiff + } + return String(a.name || '').localeCompare(String(b.name || ''), 'zh-CN') +}) + +const sortSchedules = (list) => clone(list).sort((a, b) => { + const yearDiff = Number(a.year || 0) - Number(b.year || 0) + if (yearDiff !== 0) { + return yearDiff + } + const monthDiff = Number(a.month || 0) - Number(b.month || 0) + if (monthDiff !== 0) { + return monthDiff + } + return String(a.title || '').localeCompare(String(b.title || ''), 'zh-CN') +}) + +export function listOverviewData() { + return Promise.resolve({ + systems: sortByName(store.systems), + courses: sortByName(store.courses), + schedules: sortSchedules(store.schedules) + }) +} + +export function saveSystem(data) { + if (data.id) { + const index = store.systems.findIndex((item) => String(item.id) === String(data.id)) + if (index > -1) { + store.systems.splice(index, 1, { + ...store.systems[index], + name: data.name, + sort: Number(data.sort || 0) + }) + } + } else { + store.systems.push({ + id: nextId(store.systems), + name: data.name, + sort: Number(data.sort || 0) + }) + } + persist() + return listOverviewData() +} + +export function destroySystem({ id }) { + const courseIds = store.courses.filter((item) => String(item.system_id) === String(id)).map((item) => item.id) + store.systems = store.systems.filter((item) => String(item.id) !== String(id)) + store.courses = store.courses.filter((item) => String(item.system_id) !== String(id)) + store.schedules = store.schedules.filter((item) => String(item.system_id) !== String(id) && !courseIds.includes(item.course_id)) + persist() + return listOverviewData() +} + +export function saveCourse(data) { + if (data.id) { + const index = store.courses.findIndex((item) => String(item.id) === String(data.id)) + if (index > -1) { + const previousCourse = store.courses[index] + store.courses.splice(index, 1, { + ...previousCourse, + system_id: Number(data.system_id), + name: data.name, + sort: Number(data.sort || 0) + }) + + if (String(previousCourse.system_id) !== String(data.system_id)) { + store.schedules = store.schedules.map((item) => { + if (String(item.course_id) === String(data.id)) { + return { + ...item, + system_id: Number(data.system_id) + } + } + return item + }) + } + } + } else { + store.courses.push({ + id: nextId(store.courses), + system_id: Number(data.system_id), + name: data.name, + sort: Number(data.sort || 0) + }) + } + persist() + return listOverviewData() +} + +export function destroyCourse({ id }) { + store.courses = store.courses.filter((item) => String(item.id) !== String(id)) + store.schedules = store.schedules.filter((item) => String(item.course_id) !== String(id)) + persist() + return listOverviewData() +} + +export function saveSchedule(data) { + const payload = { + year: String(data.year || getCurrentYear()), + system_id: Number(data.system_id), + course_id: Number(data.course_id), + month: Number(data.month), + title: data.title, + owner: data.owner, + location: data.location, + count_text: data.count_text || '' + } + + if (data.id) { + const index = store.schedules.findIndex((item) => String(item.id) === String(data.id)) + if (index > -1) { + store.schedules.splice(index, 1, { + ...store.schedules[index], + ...payload + }) + } + } else { + store.schedules.push({ + id: nextId(store.schedules), + ...payload + }) + } + + persist() + return listOverviewData() +} + +export function destroySchedule({ id }) { + store.schedules = store.schedules.filter((item) => String(item.id) !== String(id)) + persist() + return listOverviewData() +} diff --git a/src/views/system/role.vue b/src/views/system/role.vue index 60b2696..7e86fca 100644 --- a/src/views/system/role.vue +++ b/src/views/system/role.vue @@ -39,19 +39,24 @@ - + - - - - - 所有 - 部门 - 私有 - + + + + + 所有 + 部门 + 私有 + @@ -117,7 +122,7 @@ form: { name: "", id: "", - sortnumber: "0", + sortnumber: "0", allow_level:0 }, userdata: [], @@ -249,4 +254,4 @@ + diff --git a/src/views/system/user.vue b/src/views/system/user.vue index 9448c83..b61481d 100644 --- a/src/views/system/user.vue +++ b/src/views/system/user.vue @@ -48,7 +48,12 @@ - + diff --git a/vue.config.js b/vue.config.js index 5bf5d83..798f309 100644 --- a/vue.config.js +++ b/vue.config.js @@ -27,10 +27,8 @@ module.exports = { * */ publicPath: process.env.ENV === 'staging' ? '/admin' : '/admin', - // 测试 - outputDir: '/Users/mac/Documents/朗业/2025/s-苏州科技商学院/wx.sstbc.com/public/admin', - // 正式 - // outputDir: '/Users/mac/Documents/朗业/2024/s-苏州科技商学院/wx.sstbc.com/public/admin', + // 前台打包输出到当前机器的 /Users/apple/www/wx.sstbc.com/public/admin 目录下 + outputDir: '/Users/apple/www/wx.sstbc.com/public/admin', assetsDir: 'static', css: { loaderOptions: { // 向 CSS 相关的 loader 传递选项