1
This commit is contained in:
13
.vscode/launch.json
vendored
Normal file
13
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Program",
|
||||||
|
"program": "${workspaceFolder}/app.js",
|
||||||
|
"request": "launch",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"type": "node"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
-- WechatAdminWeb 订阅模块业务表(MySQL 8+)
|
-- WechatAdminWeb 订阅模块业务表(MySQL 8+)
|
||||||
|
-- 业务用户物理表名:biz_user(与 Sequelize 模型 biz_user、freezeTableName 一致)
|
||||||
-- 执行前请确认库名;与 api/model/biz_*.js 字段一致
|
-- 执行前请确认库名;与 api/model/biz_*.js 字段一致
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
-- 业务用户(与 sys_user 后台账号区分)
|
-- 业务用户(与 sys_user 后台账号区分)
|
||||||
CREATE TABLE IF NOT EXISTS `biz_users` (
|
CREATE TABLE IF NOT EXISTS `biz_user` (
|
||||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
`name` VARCHAR(100) NOT NULL DEFAULT '',
|
`name` VARCHAR(100) NOT NULL DEFAULT '',
|
||||||
`mobile` VARCHAR(20) NULL DEFAULT NULL,
|
`mobile` VARCHAR(20) NULL DEFAULT NULL,
|
||||||
@@ -14,8 +15,8 @@ CREATE TABLE IF NOT EXISTS `biz_users` (
|
|||||||
`created_at` DATETIME NOT NULL,
|
`created_at` DATETIME NOT NULL,
|
||||||
`updated_at` DATETIME NOT NULL,
|
`updated_at` DATETIME NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `idx_biz_users_mobile` (`mobile`),
|
KEY `idx_biz_user_mobile` (`mobile`),
|
||||||
KEY `idx_biz_users_status` (`status`)
|
KEY `idx_biz_user_status` (`status`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务用户';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务用户';
|
||||||
|
|
||||||
-- 套餐
|
-- 套餐
|
||||||
@@ -56,7 +57,7 @@ CREATE TABLE IF NOT EXISTS `biz_subscriptions` (
|
|||||||
KEY `idx_biz_sub_user` (`user_id`),
|
KEY `idx_biz_sub_user` (`user_id`),
|
||||||
KEY `idx_biz_sub_plan` (`plan_id`),
|
KEY `idx_biz_sub_plan` (`plan_id`),
|
||||||
KEY `idx_biz_sub_status_end` (`status`, `end_time`),
|
KEY `idx_biz_sub_status_end` (`status`, `end_time`),
|
||||||
CONSTRAINT `fk_biz_sub_user` FOREIGN KEY (`user_id`) REFERENCES `biz_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
CONSTRAINT `fk_biz_sub_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
CONSTRAINT `fk_biz_sub_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
CONSTRAINT `fk_biz_sub_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订阅';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订阅';
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ CREATE TABLE IF NOT EXISTS `biz_api_tokens` (
|
|||||||
UNIQUE KEY `uk_biz_token_hash` (`token_hash`),
|
UNIQUE KEY `uk_biz_token_hash` (`token_hash`),
|
||||||
KEY `idx_biz_token_user` (`user_id`),
|
KEY `idx_biz_token_user` (`user_id`),
|
||||||
KEY `idx_biz_token_plan` (`plan_id`),
|
KEY `idx_biz_token_plan` (`plan_id`),
|
||||||
CONSTRAINT `fk_biz_token_user` FOREIGN KEY (`user_id`) REFERENCES `biz_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
CONSTRAINT `fk_biz_token_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
CONSTRAINT `fk_biz_token_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
|
CONSTRAINT `fk_biz_token_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='API Token';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='API Token';
|
||||||
|
|
||||||
@@ -96,6 +97,6 @@ CREATE TABLE IF NOT EXISTS `biz_usage_monthly` (
|
|||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `uk_biz_usage_user_month` (`user_id`, `stat_month`),
|
UNIQUE KEY `uk_biz_usage_user_month` (`user_id`, `stat_month`),
|
||||||
KEY `idx_biz_usage_plan` (`plan_id`),
|
KEY `idx_biz_usage_plan` (`plan_id`),
|
||||||
CONSTRAINT `fk_biz_usage_user` FOREIGN KEY (`user_id`) REFERENCES `biz_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
CONSTRAINT `fk_biz_usage_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
CONSTRAINT `fk_biz_usage_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
CONSTRAINT `fk_biz_usage_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='月用量';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='月用量';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
-- =============================================================================
|
-- =============================================================================
|
||||||
-- sys_menu 订阅模块菜单插入脚本(字段与 api/model/sys_menu.js 一致)
|
-- sys_menu 订阅模块菜单插入脚本(字段与 api/model/sys_menu.js 一致)
|
||||||
|
-- path:不含斜杠(与首页 path=home 等约定一致);component 仍为 subscription/xxx 供前端映射
|
||||||
-- 执行前请备份。若已存在同名「订阅管理」父菜单,请先删除子菜单再删父级,或改下面名称。
|
-- 执行前请备份。若已存在同名「订阅管理」父菜单,请先删除子菜单再删父级,或改下面名称。
|
||||||
-- 若数据库表另有 created_at / updated_at 等列,请在 INSERT 中补全或给默认值。
|
-- 若数据库表另有 created_at / updated_at 等列,请在 INSERT 中补全或给默认值。
|
||||||
-- =============================================================================
|
-- =============================================================================
|
||||||
@@ -15,7 +16,7 @@ VALUES
|
|||||||
'订阅管理',
|
'订阅管理',
|
||||||
0,
|
0,
|
||||||
'ios-apps',
|
'ios-apps',
|
||||||
'/subscription',
|
'subscription',
|
||||||
'菜单',
|
'菜单',
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -31,14 +32,14 @@ SET @sub_parent_id = LAST_INSERT_ID();
|
|||||||
INSERT INTO sys_menu
|
INSERT INTO sys_menu
|
||||||
(name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort)
|
(name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort)
|
||||||
VALUES
|
VALUES
|
||||||
('运营看板', @sub_parent_id, 'ios-speedometer', '/subscription/dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 10),
|
('运营看板', @sub_parent_id, 'ios-speedometer', 'subscription_dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 10),
|
||||||
('业务用户', @sub_parent_id, 'ios-people', '/subscription/user', '页面', 0, 0, 'subscription/user', '', 1, 1, 20),
|
('业务用户', @sub_parent_id, 'ios-people', 'subscription_user', '页面', 0, 0, 'subscription/user', '', 1, 1, 20),
|
||||||
('套餐管理', @sub_parent_id, 'ios-pricetags', '/subscription/plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 30),
|
('套餐管理', @sub_parent_id, 'ios-pricetags', 'subscription_plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 30),
|
||||||
('订阅列表', @sub_parent_id, 'ios-list', '/subscription/subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 40),
|
('订阅列表', @sub_parent_id, 'ios-list', 'subscription_subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 40),
|
||||||
('API Token', @sub_parent_id, 'ios-key', '/subscription/token', '页面', 0, 0, 'subscription/token', '', 1, 1, 50),
|
('API Token', @sub_parent_id, 'ios-key', 'subscription_token', '页面', 0, 0, 'subscription/token', '', 1, 1, 50),
|
||||||
('支付确认', @sub_parent_id, 'ios-cash', '/subscription/payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 60),
|
('支付确认', @sub_parent_id, 'ios-cash', 'subscription_payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 60),
|
||||||
('月用量', @sub_parent_id, 'ios-analytics', '/subscription/usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 70),
|
('月用量', @sub_parent_id, 'ios-analytics', 'subscription_usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 70),
|
||||||
('审计日志', @sub_parent_id, 'ios-paper', '/subscription/audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 80);
|
('审计日志', @sub_parent_id, 'ios-paper', 'subscription_audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 80);
|
||||||
|
|
||||||
-- -----------------------------------------------------------------------------
|
-- -----------------------------------------------------------------------------
|
||||||
-- 方案二(可选):全部挂在根节点 parent_id=0,无父级目录(与 component-map 仍一致)
|
-- 方案二(可选):全部挂在根节点 parent_id=0,无父级目录(与 component-map 仍一致)
|
||||||
@@ -48,12 +49,12 @@ VALUES
|
|||||||
INSERT INTO sys_menu
|
INSERT INTO sys_menu
|
||||||
(name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort)
|
(name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort)
|
||||||
VALUES
|
VALUES
|
||||||
('运营看板', 0, 'ios-speedometer', '/subscription/dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 910),
|
('运营看板', 0, 'ios-speedometer', 'subscription_dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 910),
|
||||||
('业务用户', 0, 'ios-people', '/subscription/user', '页面', 0, 0, 'subscription/user', '', 1, 1, 920),
|
('业务用户', 0, 'ios-people', 'subscription_user', '页面', 0, 0, 'subscription/user', '', 1, 1, 920),
|
||||||
('套餐管理', 0, 'ios-pricetags', '/subscription/plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 930),
|
('套餐管理', 0, 'ios-pricetags', 'subscription_plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 930),
|
||||||
('订阅列表', 0, 'ios-list', '/subscription/subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 940),
|
('订阅列表', 0, 'ios-list', 'subscription_subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 940),
|
||||||
('API Token', 0, 'ios-key', '/subscription/token', '页面', 0, 0, 'subscription/token', '', 1, 1, 950),
|
('API Token', 0, 'ios-key', 'subscription_token', '页面', 0, 0, 'subscription/token', '', 1, 1, 950),
|
||||||
('支付确认', 0, 'ios-cash', '/subscription/payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 960),
|
('支付确认', 0, 'ios-cash', 'subscription_payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 960),
|
||||||
('月用量', 0, 'ios-analytics', '/subscription/usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 970),
|
('月用量', 0, 'ios-analytics', 'subscription_usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 970),
|
||||||
('审计日志', 0, 'ios-paper', '/subscription/audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 980);
|
('审计日志', 0, 'ios-paper', 'subscription_audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 980);
|
||||||
*/
|
*/
|
||||||
|
|||||||
29
_docs/sql/003_home_menu_seed.sql
Normal file
29
_docs/sql/003_home_menu_seed.sql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- sys_menu 首页(字段与 api/model/sys_menu.js、现有库中菜单数据格式一致)
|
||||||
|
--
|
||||||
|
-- 参考库中同类数据示例:
|
||||||
|
-- path:首页为 home(无前导 /);订阅子页为 /subscription/xxx
|
||||||
|
-- component:与 admin/src/router/component-map.js 的 key 一致,可为 home/index 或 home/index.vue
|
||||||
|
-- icon:Material 图标名如 md-home(与系统页 system/sys_user.vue 等同风格)
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
INSERT INTO sys_menu
|
||||||
|
(name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
'首页',
|
||||||
|
0,
|
||||||
|
'md-home',
|
||||||
|
'home',
|
||||||
|
'页面',
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
'home/index.vue',
|
||||||
|
'',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 若已存在 name=首页 或 path=home 的记录,请先删除或改值后再执行,避免重复。
|
||||||
|
-- 若你希望 component 不带后缀,可将上面 'home/index.vue' 改为 'home/index'(component-map 两种 key 均已注册)。
|
||||||
36
_docs/sql/004_migrate_biz_users_to_biz_user.sql
Normal file
36
_docs/sql/004_migrate_biz_users_to_biz_user.sql
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- 若早期已按旧版 001 建表 biz_users,而运行时报错查 biz_user,可执行本脚本迁移表名。
|
||||||
|
-- 执行前备份数据库。若子表尚未创建,可跳过本脚本,直接 DROP biz_users 后重跑新版 001_biz_schema.sql。
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- 若不存在 biz_users 则无需执行
|
||||||
|
-- 步骤:去外键引用 -> 重命名父表 ->(子表 FK 仍指向旧名时需重建,MySQL 8 重命名父表后约束名可能需检查)
|
||||||
|
|
||||||
|
-- 1) 删除引用 biz_users 的外键(子表若已存在)
|
||||||
|
SET @db = DATABASE();
|
||||||
|
|
||||||
|
SET @sql = (
|
||||||
|
SELECT GROUP_CONCAT(CONCAT('ALTER TABLE `', TABLE_NAME, '` DROP FOREIGN KEY `', CONSTRAINT_NAME, '`') SEPARATOR '; ')
|
||||||
|
FROM information_schema.KEY_COLUMN_USAGE
|
||||||
|
WHERE TABLE_SCHEMA = @db
|
||||||
|
AND REFERENCED_TABLE_NAME = 'biz_users'
|
||||||
|
);
|
||||||
|
-- 若上面为空则无子表 FK,可手动执行下面 RENAME
|
||||||
|
|
||||||
|
-- 手工示例(按实际约束名调整):
|
||||||
|
-- ALTER TABLE `biz_subscriptions` DROP FOREIGN KEY `fk_biz_sub_user`;
|
||||||
|
-- ALTER TABLE `biz_api_tokens` DROP FOREIGN KEY `fk_biz_token_user`;
|
||||||
|
-- ALTER TABLE `biz_usage_monthly` DROP FOREIGN KEY `fk_biz_usage_user`;
|
||||||
|
|
||||||
|
-- 2) 重命名业务用户表
|
||||||
|
-- RENAME TABLE `biz_users` TO `biz_user`;
|
||||||
|
|
||||||
|
-- 3) 重新添加外键(与 001_biz_schema.sql 一致)
|
||||||
|
-- ALTER TABLE `biz_subscriptions`
|
||||||
|
-- ADD CONSTRAINT `fk_biz_sub_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
-- ALTER TABLE `biz_api_tokens`
|
||||||
|
-- ADD CONSTRAINT `fk_biz_token_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
-- ALTER TABLE `biz_usage_monthly`
|
||||||
|
-- ADD CONSTRAINT `fk_biz_usage_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- 更简单做法(无重要数据):DROP 子表与 biz_users,再执行新版 001_biz_schema.sql 全量重建。
|
||||||
@@ -39,8 +39,8 @@ admin/
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
apiUrl: 'http://localhost:9098/admin_api/',
|
apiUrl: 'http://localhost:9099/admin_api/',
|
||||||
uploadUrl: 'http://localhost:9098/admin_api/upload',
|
uploadUrl: 'http://localhost:9099/admin_api/upload',
|
||||||
debug: true // 开发环境显示调试信息
|
debug: true // 开发环境显示调试信息
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -114,7 +114,7 @@ HTTP 工具已自动使用 `apiUrl` 作为基础路径:
|
|||||||
```javascript
|
```javascript
|
||||||
// 无需手动拼接 apiUrl,框架会自动处理
|
// 无需手动拼接 apiUrl,框架会自动处理
|
||||||
this.$http.get('/user/list')
|
this.$http.get('/user/list')
|
||||||
// 实际请求: http://localhost:9098/admin_api/user/list
|
// 实际请求: http://localhost:9099/admin_api/user/list
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -141,8 +141,8 @@ const baseConfig = {
|
|||||||
// 开发环境配置
|
// 开发环境配置
|
||||||
const developmentConfig = {
|
const developmentConfig = {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
apiUrl: 'http://localhost:9098/admin_api/', // 修改开发环境 API 地址
|
apiUrl: 'http://localhost:9099/admin_api/', // 修改开发环境 API 地址
|
||||||
uploadUrl: 'http://localhost:9098/admin_api/upload',
|
uploadUrl: 'http://localhost:9099/admin_api/upload',
|
||||||
debug: true
|
debug: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +162,8 @@ const productionConfig = {
|
|||||||
```javascript
|
```javascript
|
||||||
const developmentConfig = {
|
const developmentConfig = {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
apiUrl: 'http://localhost:9098/admin_api/',
|
apiUrl: 'http://localhost:9099/admin_api/',
|
||||||
uploadUrl: 'http://localhost:9098/admin_api/upload',
|
uploadUrl: 'http://localhost:9099/admin_api/upload',
|
||||||
|
|
||||||
// 自定义配置
|
// 自定义配置
|
||||||
enableMock: true,
|
enableMock: true,
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const baseConfig = {
|
|||||||
// 本地开发(默认)
|
// 本地开发(默认)
|
||||||
const developmentConfig = {
|
const developmentConfig = {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
apiUrl: 'http://localhost:9098/admin_api/',
|
apiUrl: 'http://localhost:9099/admin_api/',
|
||||||
uploadUrl: 'http://localhost:9098/admin_api/upload',
|
uploadUrl: 'http://localhost:9099/admin_api/upload',
|
||||||
debug: true
|
debug: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// 组件映射表:后端菜单返回的 component 路径需与此处 key 一致(不含 .vue)
|
// 组件映射表:后端菜单返回的 component 路径需与此处 key 一致(不含 .vue)
|
||||||
|
import HomeIndex from '../views/home/index.vue'
|
||||||
import TestPage from '../views/test/test.vue'
|
import TestPage from '../views/test/test.vue'
|
||||||
import SubscriptionDashboard from '../views/subscription/dashboard.vue'
|
import SubscriptionDashboard from '../views/subscription/dashboard.vue'
|
||||||
import SubscriptionUsers from '../views/subscription/users.vue'
|
import SubscriptionUsers from '../views/subscription/users.vue'
|
||||||
@@ -10,6 +11,9 @@ import SubscriptionUsage from '../views/subscription/usage.vue'
|
|||||||
import SubscriptionAuditLog from '../views/subscription/audit_log.vue'
|
import SubscriptionAuditLog from '../views/subscription/audit_log.vue'
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
|
// 与 sys_menu.component 一致:库中常见为 home/index 或 home/index.vue
|
||||||
|
'home/index': HomeIndex,
|
||||||
|
'home/index.vue': HomeIndex,
|
||||||
'test/test': TestPage,
|
'test/test': TestPage,
|
||||||
'subscription/dashboard': SubscriptionDashboard,
|
'subscription/dashboard': SubscriptionDashboard,
|
||||||
'subscription/user': SubscriptionUsers,
|
'subscription/user': SubscriptionUsers,
|
||||||
|
|||||||
53
admin/src/views/home/index.vue
Normal file
53
admin/src/views/home/index.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-view">
|
||||||
|
<div class="table-head-tool">
|
||||||
|
<div class="table-title-row">
|
||||||
|
<h2 class="table-title">首页</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-body home-body">
|
||||||
|
<Card dis-hover>
|
||||||
|
<p class="home-tip">欢迎使用管理后台。</p>
|
||||||
|
<p class="home-muted">菜单由 <code>sys_menu</code> 配置:<code>path</code> 与路由一致,<code>component</code> 与 <code>component-map.js</code> 的 key 一致(如 <code>home/index</code>)。</p>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'HomeIndex',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.content-view {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.table-head-tool {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.table-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.home-body {
|
||||||
|
max-width: 720px;
|
||||||
|
}
|
||||||
|
.home-tip {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.home-muted {
|
||||||
|
margin: 0;
|
||||||
|
color: #808695;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,24 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<div class="sub-toolbar">
|
<div class="table-head-tool">
|
||||||
<h2 class="sub-title">审计日志</h2>
|
<div class="table-title-row">
|
||||||
<Button type="primary" @click="load(1)">刷新</Button>
|
<h2 class="table-title">审计日志</h2>
|
||||||
<Button class="ml8" @click="doExport">导出 CSV</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-search">
|
|
||||||
<Form inline>
|
<Form ref="formInline" :model="param.seachOption" inline :label-width="80">
|
||||||
<FormItem label="动作">
|
<FormItem label="动作">
|
||||||
<Select v-model="param.seachOption.key" style="width: 140px">
|
<Select v-model="param.seachOption.key" style="width: 140px">
|
||||||
<Option value="action">action</Option>
|
<Option value="action">action</Option>
|
||||||
<Option value="resource_type">resource_type</Option>
|
<Option value="resource_type">resource_type</Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Input v-model="param.seachOption.value" class="ml8" style="width: 220px" placeholder="模糊/精确" />
|
<Input v-model="param.seachOption.value" class="ml10" style="width: 220px" placeholder="模糊/精确" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
<Button type="primary" @click="load(1)">查询</Button>
|
<Button type="primary" @click="load(1)">查询</Button>
|
||||||
|
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||||
|
<Button type="default" @click="doExport" class="ml10">导出 CSV</Button>
|
||||||
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="table-body">
|
||||||
<Table :columns="columns" :data="rows" border stripe />
|
<Table :columns="columns" :data="rows" border stripe />
|
||||||
<div class="sub-page-bar">
|
<div class="table-page-bar">
|
||||||
<Page
|
<Page
|
||||||
:total="total"
|
:total="total"
|
||||||
:current="param.pageOption.page"
|
:current="param.pageOption.page"
|
||||||
@@ -29,6 +34,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -105,26 +111,43 @@ export default {
|
|||||||
this.$Message.error((res && res.message) || '导出失败')
|
this.$Message.error((res && res.message) || '导出失败')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.param.seachOption = { key: 'action', value: '' }
|
||||||
|
this.load(1)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
|
||||||
display: inline-block;
|
.table-head-tool {
|
||||||
margin: 0 16px 0 0;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
.ml8 {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
.sub-search {
|
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
.sub-page-bar {
|
|
||||||
|
.table-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml10 {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-body {
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-page-bar {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<div class="sub-toolbar">
|
<div class="table-head-tool">
|
||||||
<h2 class="sub-title">订阅运营看板</h2>
|
<div class="table-title-row">
|
||||||
|
<h2 class="table-title">订阅运营看板</h2>
|
||||||
<Button type="primary" @click="load">刷新</Button>
|
<Button type="primary" @click="load">刷新</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-body">
|
||||||
<Row :gutter="16" v-if="stats">
|
<Row :gutter="16" v-if="stats">
|
||||||
<Col span="6">
|
<Col span="6">
|
||||||
<Card dis-hover><p class="lbl">业务用户总数</p><p class="num">{{ stats.users.total }}</p></Card>
|
<Card dis-hover><p class="lbl">业务用户总数</p><p class="num">{{ stats.users.total }}</p></Card>
|
||||||
@@ -35,6 +39,7 @@
|
|||||||
<p v-else class="muted">加载中…</p>
|
<p v-else class="muted">加载中…</p>
|
||||||
<p class="muted small">数据时间:{{ stats && stats.server_time }}</p>
|
<p class="muted small">数据时间:{{ stats && stats.server_time }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -64,12 +69,23 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
|
||||||
display: inline-block;
|
.table-head-tool {
|
||||||
margin: 0 16px 0 0;
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
.lbl {
|
.lbl {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<h2 class="sub-title">支付确认(轻量)</h2>
|
<h2 class="sub-title">支付确认(轻量)</h2>
|
||||||
<p class="sub-desc">将 <code>pending</code> 订阅置为 <code>active</code>,并写入支付单号。</p>
|
<p class="sub-desc">将 <code>pending</code> 订阅置为 <code>active</code>,并写入支付单号。</p>
|
||||||
<Card dis-hover title="线下确认" style="max-width: 520px; margin-bottom: 16px">
|
<Card dis-hover title="线下确认" style="max-width: 520px; margin-bottom: 16px">
|
||||||
@@ -86,7 +86,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
.sub-title {
|
||||||
|
|||||||
@@ -1,26 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<div class="sub-toolbar">
|
<div class="table-head-tool">
|
||||||
<h2 class="sub-title">套餐</h2>
|
<div class="table-title-row">
|
||||||
|
<h2 class="table-title">套餐</h2>
|
||||||
<Button type="primary" @click="openEdit(null)">新增套餐</Button>
|
<Button type="primary" @click="openEdit(null)">新增套餐</Button>
|
||||||
<Button class="ml8" @click="load(1)">刷新</Button>
|
|
||||||
<Button class="ml8" @click="doExport">导出 CSV</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-search">
|
|
||||||
<Form inline :label-width="70">
|
<Form ref="formInline" :model="param.seachOption" inline :label-width="80">
|
||||||
<FormItem label="条件">
|
<FormItem label="条件">
|
||||||
<Select v-model="param.seachOption.key" style="width: 140px">
|
<Select v-model="param.seachOption.key" style="width: 140px">
|
||||||
<Option value="plan_code">编码</Option>
|
<Option value="plan_code">编码</Option>
|
||||||
<Option value="plan_name">名称</Option>
|
<Option value="plan_name">名称</Option>
|
||||||
<Option value="status">状态</Option>
|
<Option value="status">状态</Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Input v-model="param.seachOption.value" class="ml8" style="width: 220px" search @on-search="load(1)" />
|
<Input
|
||||||
|
v-model="param.seachOption.value"
|
||||||
|
class="ml10"
|
||||||
|
style="width: 220px"
|
||||||
|
search
|
||||||
|
@on-search="load(1)"
|
||||||
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
<Button type="primary" @click="load(1)">查询</Button>
|
<Button type="primary" @click="load(1)">查询</Button>
|
||||||
|
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||||
|
<Button type="default" @click="doExport" class="ml10">导出 CSV</Button>
|
||||||
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="table-body">
|
||||||
<Table :columns="columns" :data="rows" border stripe />
|
<Table :columns="columns" :data="rows" border stripe />
|
||||||
<div class="sub-page-bar">
|
<div class="table-page-bar">
|
||||||
<Page
|
<Page
|
||||||
:total="total"
|
:total="total"
|
||||||
:current="param.pageOption.page"
|
:current="param.pageOption.page"
|
||||||
@@ -30,6 +41,7 @@
|
|||||||
@on-page-size-change="onSize"
|
@on-page-size-change="onSize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Modal v-model="modal" :title="form.id ? '编辑套餐' : '新增套餐'" width="720" :loading="saving" @on-ok="save">
|
<Modal v-model="modal" :title="form.id ? '编辑套餐' : '新增套餐'" width="720" :loading="saving" @on-ok="save">
|
||||||
<Form ref="formRef" :model="form" :rules="rules" :label-width="120">
|
<Form ref="formRef" :model="form" :rules="rules" :label-width="120">
|
||||||
@@ -239,30 +251,47 @@ export default {
|
|||||||
this.$Message.error((res && res.message) || '导出失败')
|
this.$Message.error((res && res.message) || '导出失败')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.param.seachOption = { key: 'plan_code', value: '' }
|
||||||
|
this.load(1)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-toolbar {
|
|
||||||
|
.table-head-tool {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
|
||||||
display: inline-block;
|
.table-title-row {
|
||||||
margin: 0 16px 0 0;
|
display: flex;
|
||||||
font-size: 18px;
|
align-items: center;
|
||||||
vertical-align: middle;
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml10 {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.ml8 {
|
.ml8 {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
.sub-search {
|
|
||||||
margin-bottom: 12px;
|
.table-body {
|
||||||
}
|
}
|
||||||
.sub-page-bar {
|
|
||||||
|
.table-page-bar {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<div class="sub-toolbar">
|
<div class="table-head-tool">
|
||||||
<h2 class="sub-title">订阅</h2>
|
<div class="table-title-row">
|
||||||
|
<h2 class="table-title">订阅</h2>
|
||||||
<Button type="primary" @click="openOpen">开通订阅</Button>
|
<Button type="primary" @click="openOpen">开通订阅</Button>
|
||||||
<Button class="ml8" @click="load(1)">刷新</Button>
|
|
||||||
<Button class="ml8" @click="doExport">导出 CSV</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-search">
|
|
||||||
<Form inline>
|
<Form ref="formInline" :model="param.seachOption" inline :label-width="80">
|
||||||
<FormItem label="用户ID">
|
<FormItem label="用户ID">
|
||||||
<Input v-model="param.seachOption.value" style="width: 140px" placeholder="筛选 user_id" />
|
<Input v-model="param.seachOption.value" style="width: 140px" placeholder="筛选 user_id" class="ml10" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Select v-model="param.seachOption.key" style="width: 120px">
|
<Select v-model="param.seachOption.key" style="width: 120px">
|
||||||
@@ -17,11 +16,17 @@
|
|||||||
<Option value="status">状态</Option>
|
<Option value="status">状态</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
<Button type="primary" @click="load(1)">查询</Button>
|
<Button type="primary" @click="load(1)">查询</Button>
|
||||||
|
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||||
|
<Button type="default" @click="doExport" class="ml10">导出 CSV</Button>
|
||||||
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="table-body">
|
||||||
<Table :columns="columns" :data="rows" border stripe />
|
<Table :columns="columns" :data="rows" border stripe />
|
||||||
<div class="sub-page-bar">
|
<div class="table-page-bar">
|
||||||
<Page
|
<Page
|
||||||
:total="total"
|
:total="total"
|
||||||
:current="param.pageOption.page"
|
:current="param.pageOption.page"
|
||||||
@@ -31,6 +36,7 @@
|
|||||||
@on-page-size-change="onSize"
|
@on-page-size-change="onSize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Modal v-model="openModal" title="开通订阅" width="640" :loading="saving" @on-ok="submitOpen">
|
<Modal v-model="openModal" title="开通订阅" width="640" :loading="saving" @on-ok="submitOpen">
|
||||||
<Form :label-width="110">
|
<Form :label-width="110">
|
||||||
@@ -264,27 +270,44 @@ export default {
|
|||||||
this.$Message.error((res && res.message) || '导出失败')
|
this.$Message.error((res && res.message) || '导出失败')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.param.seachOption = { key: 'user_id', value: '' }
|
||||||
|
this.load(1)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-toolbar {
|
|
||||||
|
.table-head-tool {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
|
||||||
display: inline-block;
|
.table-title-row {
|
||||||
margin: 0 16px 0 0;
|
display: flex;
|
||||||
font-size: 18px;
|
align-items: center;
|
||||||
vertical-align: middle;
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml10 {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.ml8 {
|
.ml8 {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
.sub-page-bar {
|
|
||||||
|
.table-page-bar {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<div class="sub-toolbar">
|
<div class="table-head-tool">
|
||||||
<h2 class="sub-title">API Token</h2>
|
<div class="table-title-row">
|
||||||
|
<h2 class="table-title">API Token</h2>
|
||||||
<Button type="primary" @click="openCreate">创建 Token</Button>
|
<Button type="primary" @click="openCreate">创建 Token</Button>
|
||||||
<Button class="ml8" @click="load(1)">刷新</Button>
|
|
||||||
<Button class="ml8" @click="doExport">导出 CSV</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-search">
|
|
||||||
<Form inline>
|
<Form ref="formInline" :model="param.seachOption" inline :label-width="80">
|
||||||
<FormItem label="用户ID">
|
<FormItem label="用户ID">
|
||||||
<Input v-model="param.seachOption.value" style="width: 140px" />
|
<Input v-model="param.seachOption.value" style="width: 140px" class="ml10" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Select v-model="param.seachOption.key" style="width: 120px">
|
<Select v-model="param.seachOption.key" style="width: 120px">
|
||||||
@@ -17,11 +16,17 @@
|
|||||||
<Option value="status">状态</Option>
|
<Option value="status">状态</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
<Button type="primary" @click="load(1)">查询</Button>
|
<Button type="primary" @click="load(1)">查询</Button>
|
||||||
|
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||||
|
<Button type="default" @click="doExport" class="ml10">导出 CSV</Button>
|
||||||
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="table-body">
|
||||||
<Table :columns="columns" :data="rows" border stripe />
|
<Table :columns="columns" :data="rows" border stripe />
|
||||||
<div class="sub-page-bar">
|
<div class="table-page-bar">
|
||||||
<Page
|
<Page
|
||||||
:total="total"
|
:total="total"
|
||||||
:current="param.pageOption.page"
|
:current="param.pageOption.page"
|
||||||
@@ -31,6 +36,7 @@
|
|||||||
@on-page-size-change="onSize"
|
@on-page-size-change="onSize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Modal v-model="createModal" title="创建 Token" width="560" :loading="saving" @on-ok="submitCreate">
|
<Modal v-model="createModal" title="创建 Token" width="560" :loading="saving" @on-ok="submitCreate">
|
||||||
<Form :label-width="100">
|
<Form :label-width="100">
|
||||||
@@ -177,27 +183,43 @@ export default {
|
|||||||
this.$Message.error((res && res.message) || '导出失败')
|
this.$Message.error((res && res.message) || '导出失败')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.param.seachOption = { key: 'user_id', value: '' }
|
||||||
|
this.load(1)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-toolbar {
|
|
||||||
|
.table-head-tool {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
|
||||||
display: inline-block;
|
.table-title-row {
|
||||||
margin: 0 16px 0 0;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
.ml8 {
|
|
||||||
margin-left: 8px;
|
.ml10 {
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
.sub-page-bar {
|
|
||||||
|
.table-body {
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-page-bar {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<div class="sub-toolbar">
|
<div class="table-head-tool">
|
||||||
<h2 class="sub-title">月用量</h2>
|
<div class="table-title-row">
|
||||||
|
<h2 class="table-title">月用量</h2>
|
||||||
<Button type="primary" @click="openEdit(null)">新增</Button>
|
<Button type="primary" @click="openEdit(null)">新增</Button>
|
||||||
<Button class="ml8" @click="load(1)">刷新</Button>
|
|
||||||
<Button class="ml8" @click="doExport">导出 CSV</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-search">
|
|
||||||
<Form inline>
|
<Form ref="formInline" :model="param.seachOption" inline :label-width="80">
|
||||||
<FormItem label="条件">
|
<FormItem label="条件">
|
||||||
<Select v-model="param.seachOption.key" style="width: 140px">
|
<Select v-model="param.seachOption.key" style="width: 140px">
|
||||||
<Option value="user_id">用户ID</Option>
|
<Option value="user_id">用户ID</Option>
|
||||||
<Option value="stat_month">月份</Option>
|
<Option value="stat_month">月份</Option>
|
||||||
<Option value="plan_id">套餐ID</Option>
|
<Option value="plan_id">套餐ID</Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Input v-model="param.seachOption.value" class="ml8" style="width: 200px" />
|
<Input v-model="param.seachOption.value" class="ml10" style="width: 200px" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
<Button type="primary" @click="load(1)">查询</Button>
|
<Button type="primary" @click="load(1)">查询</Button>
|
||||||
|
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||||
|
<Button type="default" @click="doExport" class="ml10">导出 CSV</Button>
|
||||||
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="table-body">
|
||||||
<Table :columns="columns" :data="rows" border stripe />
|
<Table :columns="columns" :data="rows" border stripe />
|
||||||
<div class="sub-page-bar">
|
<div class="table-page-bar">
|
||||||
<Page
|
<Page
|
||||||
:total="total"
|
:total="total"
|
||||||
:current="param.pageOption.page"
|
:current="param.pageOption.page"
|
||||||
@@ -30,6 +35,7 @@
|
|||||||
@on-page-size-change="onSize"
|
@on-page-size-change="onSize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Modal v-model="modal" :title="form.id ? '编辑用量' : '新增用量'" width="640" :loading="saving" @on-ok="save">
|
<Modal v-model="modal" :title="form.id ? '编辑用量' : '新增用量'" width="640" :loading="saving" @on-ok="save">
|
||||||
<Form ref="formRef" :model="form" :rules="rules" :label-width="120">
|
<Form ref="formRef" :model="form" :rules="rules" :label-width="120">
|
||||||
@@ -182,26 +188,47 @@ export default {
|
|||||||
this.$Message.error((res && res.message) || '导出失败')
|
this.$Message.error((res && res.message) || '导出失败')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.param.seachOption = { key: 'stat_month', value: '' }
|
||||||
|
this.load(1)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
|
||||||
display: inline-block;
|
.table-head-tool {
|
||||||
margin: 0 16px 0 0;
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ml10 {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.ml8 {
|
.ml8 {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
.sub-search {
|
|
||||||
margin-bottom: 12px;
|
.table-body {
|
||||||
}
|
}
|
||||||
.sub-page-bar {
|
|
||||||
|
.table-page-bar {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sub-page">
|
<div class="content-view">
|
||||||
<div class="sub-toolbar">
|
<div class="table-head-tool">
|
||||||
<h2 class="sub-title">业务用户</h2>
|
<div class="table-title-row">
|
||||||
|
<h2 class="table-title">业务用户</h2>
|
||||||
<Button type="primary" @click="openEdit(null)">新增</Button>
|
<Button type="primary" @click="openEdit(null)">新增</Button>
|
||||||
<Button class="ml8" @click="load(1)">刷新</Button>
|
|
||||||
<Button class="ml8" @click="doExport">导出 CSV</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-search">
|
|
||||||
<Form inline :label-width="70">
|
<Form ref="formInline" :model="param.seachOption" inline :label-width="80">
|
||||||
<FormItem label="条件">
|
<FormItem label="条件">
|
||||||
<Select v-model="param.seachOption.key" style="width: 140px">
|
<Select v-model="param.seachOption.key" style="width: 140px">
|
||||||
<Option value="mobile">手机</Option>
|
<Option value="mobile">手机</Option>
|
||||||
<Option value="company_name">公司</Option>
|
<Option value="company_name">公司</Option>
|
||||||
<Option value="status">状态</Option>
|
<Option value="status">状态</Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Input v-model="param.seachOption.value" placeholder="关键字" style="width: 220px" class="ml8" search @on-search="load(1)" />
|
<Input
|
||||||
|
v-model="param.seachOption.value"
|
||||||
|
placeholder="关键字"
|
||||||
|
style="width: 220px"
|
||||||
|
class="ml10"
|
||||||
|
search
|
||||||
|
@on-search="load(1)"
|
||||||
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Button type="primary" @click="load(1)">查询</Button>
|
<Button type="primary" @click="load(1)">查询</Button>
|
||||||
|
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||||
|
<Button type="default" @click="doExport" class="ml10">导出 CSV</Button>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="table-body">
|
||||||
<Table :columns="columns" :data="rows" border stripe />
|
<Table :columns="columns" :data="rows" border stripe />
|
||||||
<div class="sub-page-bar">
|
<div class="table-page-bar">
|
||||||
<Page
|
<Page
|
||||||
:total="total"
|
:total="total"
|
||||||
:current="param.pageOption.page"
|
:current="param.pageOption.page"
|
||||||
@@ -32,6 +42,7 @@
|
|||||||
@on-page-size-change="onSize"
|
@on-page-size-change="onSize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Modal v-model="modal" :title="form.id ? '编辑用户' : '新增用户'" width="640" :loading="saving" @on-ok="save">
|
<Modal v-model="modal" :title="form.id ? '编辑用户' : '新增用户'" width="640" :loading="saving" @on-ok="save">
|
||||||
<Form ref="formRef" :model="form" :rules="rules" :label-width="100">
|
<Form ref="formRef" :model="form" :rules="rules" :label-width="100">
|
||||||
@@ -276,30 +287,47 @@ export default {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.param.seachOption = { key: 'mobile', value: '' }
|
||||||
|
this.load(1)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sub-page {
|
.content-view {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.sub-toolbar {
|
|
||||||
|
.table-head-tool {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
.sub-title {
|
|
||||||
display: inline-block;
|
.table-title-row {
|
||||||
margin: 0 16px 0 0;
|
display: flex;
|
||||||
font-size: 18px;
|
align-items: center;
|
||||||
vertical-align: middle;
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml10 {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.ml8 {
|
.ml8 {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
.sub-search {
|
|
||||||
margin-bottom: 12px;
|
.table-body {
|
||||||
}
|
}
|
||||||
.sub-page-bar {
|
|
||||||
|
.table-page-bar {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const Sequelize = require("sequelize");
|
const Sequelize = require("sequelize");
|
||||||
|
|
||||||
module.exports = (db) => {
|
module.exports = (db) => {
|
||||||
return db.define(
|
const biz_api_token = db.define(
|
||||||
"biz_api_token",
|
"biz_api_token",
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
@@ -43,4 +43,6 @@ module.exports = (db) => {
|
|||||||
comment: "API Token",
|
comment: "API Token",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// biz_api_token.sync({ alter: true });
|
||||||
|
return biz_api_token;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const Sequelize = require("sequelize");
|
const Sequelize = require("sequelize");
|
||||||
|
|
||||||
module.exports = (db) => {
|
module.exports = (db) => {
|
||||||
return db.define(
|
const biz_audit_log = db.define(
|
||||||
"biz_audit_log",
|
"biz_audit_log",
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
@@ -44,11 +44,8 @@ module.exports = (db) => {
|
|||||||
timestamps: false,
|
timestamps: false,
|
||||||
underscored: true,
|
underscored: true,
|
||||||
comment: "审计日志",
|
comment: "审计日志",
|
||||||
hooks: {
|
|
||||||
beforeCreate(row) {
|
|
||||||
if (!row.created_at) row.created_at = new Date();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// biz_audit_log.sync({ alter: true });
|
||||||
|
return biz_audit_log;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const Sequelize = require("sequelize");
|
const Sequelize = require("sequelize");
|
||||||
|
|
||||||
module.exports = (db) => {
|
module.exports = (db) => {
|
||||||
return db.define(
|
const biz_plan = db.define(
|
||||||
"biz_plan",
|
"biz_plan",
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
@@ -53,4 +53,6 @@ module.exports = (db) => {
|
|||||||
comment: "套餐",
|
comment: "套餐",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// biz_plan.sync({ alter: true });
|
||||||
|
return biz_plan;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const Sequelize = require("sequelize");
|
const Sequelize = require("sequelize");
|
||||||
|
|
||||||
module.exports = (db) => {
|
module.exports = (db) => {
|
||||||
return db.define(
|
const biz_subscription = db.define(
|
||||||
"biz_subscription",
|
"biz_subscription",
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
@@ -45,4 +45,6 @@ module.exports = (db) => {
|
|||||||
comment: "订阅",
|
comment: "订阅",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// biz_subscription.sync({ alter: true });
|
||||||
|
return biz_subscription;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const Sequelize = require("sequelize");
|
const Sequelize = require("sequelize");
|
||||||
|
|
||||||
module.exports = (db) => {
|
module.exports = (db) => {
|
||||||
return db.define(
|
const biz_usage_monthly = db.define(
|
||||||
"biz_usage_monthly",
|
"biz_usage_monthly",
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
@@ -35,4 +35,6 @@ module.exports = (db) => {
|
|||||||
comment: "月用量",
|
comment: "月用量",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// biz_usage_monthly.sync({ alter: true });
|
||||||
|
return biz_usage_monthly;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const Sequelize = require("sequelize");
|
const Sequelize = require("sequelize");
|
||||||
|
|
||||||
module.exports = (db) => {
|
module.exports = (db) => {
|
||||||
return db.define(
|
const biz_user = db.define(
|
||||||
"biz_user",
|
"biz_user",
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
@@ -36,10 +36,15 @@ module.exports = (db) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tableName: "biz_users",
|
// 与库表名一致:单数 biz_user(与模型名一致,避免部分环境下 tableName 未生效时落到默认表名 biz_user)
|
||||||
|
tableName: "biz_user",
|
||||||
|
freezeTableName: true,
|
||||||
timestamps: true,
|
timestamps: true,
|
||||||
underscored: true,
|
underscored: true,
|
||||||
comment: "业务用户",
|
comment: "业务用户",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// biz_user.sync({ alter: true });
|
||||||
|
|
||||||
|
return biz_user;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ try {
|
|||||||
// Shared config (all environments)
|
// Shared config (all environments)
|
||||||
const baseConfig = {
|
const baseConfig = {
|
||||||
"port": {
|
"port": {
|
||||||
"node": 9098,
|
"node": 9099,
|
||||||
"web": 9090
|
"web": 9091
|
||||||
},
|
},
|
||||||
"allowUrls": [
|
"allowUrls": [
|
||||||
"/admin_api/sys_user/login",
|
"/admin_api/sys_user/login",
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ module.exports = {
|
|||||||
return 'https://example.com'; // 生产环境(请按实际域名修改)
|
return 'https://example.com'; // 生产环境(请按实际域名修改)
|
||||||
case 'development':
|
case 'development':
|
||||||
default:
|
default:
|
||||||
return 'http://localhost:9098'; // 开发环境
|
return 'http://localhost:9099'; // 开发环境
|
||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -67,7 +67,7 @@
|
|||||||
"test": "node __tests__/http_interface_test.js",
|
"test": "node __tests__/http_interface_test.js",
|
||||||
"load": "node __tests__/loadtest-both.js",
|
"load": "node __tests__/loadtest-both.js",
|
||||||
"api": " nodemon ./app.js ",
|
"api": " nodemon ./app.js ",
|
||||||
"serve": "cd ./admin&&npm run serve -- --port 9000 ",
|
"serve": "cd ./admin&&npm run dev -- --port 9001 ",
|
||||||
"build": "cd ./admin&&npm run build "
|
"build": "cd ./admin&&npm run build "
|
||||||
},
|
},
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
|||||||
Reference in New Issue
Block a user