上传文件至「/」

This commit is contained in:
2026-08-14 10:59:37 +08:00
parent 307f304425
commit 150636bdfc
5 changed files with 2607 additions and 0 deletions

180
App.vue Normal file
View File

@@ -0,0 +1,180 @@
<script>
import config from '@/app.config.js';
import { version } from './package.json';
export default {
computed: {},
methods: {
// 初始化菜单权限等数据
init() {
let { vk } = this;
// 如果token失效,直接跳登录页面
if (!vk.checkToken()) {
this.navigateToLogin();
return false;
}
if (!this.isAllowLoginBackground()) {
vk.alert('您的账户无登陆权限', () => {
this.navigateToLogin();
});
return false;
}
vk.userCenter.getMenu({
success: (data) => {
// 初始化菜单
let { menus = [] } = data;
// 合并去重
menus = vk.pubfn.arr_concat(menus, config.sideBar.staticMenu, 'menu_id');
// 排序
menus.sort((a, b) => {
let sortA = a.sort || 0;
let sortB = b.sort || 0;
return sortA - sortB;
});
if (JSON.stringify(menus) !== JSON.stringify(vk.getVuex('$app.navMenu'))) {
vk.setVuex('$app.navMenu', menus);
}
// 将树形结构转成数组结构
let menuList = vk.pubfn.treeToArray(menus, {
id: 'menu_id',
parent_id: 'parent_id',
children: 'children',
});
if (JSON.stringify(menuList) !== JSON.stringify(vk.getVuex('$app.menuList'))) {
vk.setVuex('$app.menuList', menuList);
}
vk.setVuex('$user.userInfo', data.userInfo);
vk.setVuex('$user.permission', data.userInfo.permission);
vk.setVuex('$app.inited', true);
this.checkCurrentAppId();
},
});
},
// 初始化系统环境变量
initApp() {
uni.getSystemInfo().then(([err, res]) => {
let isPC = res.model && res.model != 'PC' && res.windowWidth <= 768 ? false : true;
vk.setVuex('$app.isPC', isPC);
vk.setVuex('$app.width', res.windowWidth);
vk.setVuex('$app.height', res.windowHeight);
});
uni.onWindowResize((res) => {
vk.pubfn.debounce(
() => {
vk.setVuex('$app.width', res.size.windowWidth);
vk.setVuex('$app.height', res.size.windowHeight);
let isPC = res.size.windowWidth > 768 ? true : false;
vk.setVuex('$app.isPC', isPC);
},
50,
false,
'app-onresize'
);
});
},
// 检查是否允许登录admin后台
isAllowLoginBackground(userInfo) {
let { vk } = this;
if (!userInfo) userInfo = vk.getVuex('$user.userInfo');
let key = true;
if (vk.pubfn.isNotNull(userInfo)) {
let { role = [], allow_login_background = false } = userInfo;
if (role.indexOf('admin') == -1 && !allow_login_background) {
key = false;
}
}
return key;
},
// 检测当前应用appid是否已添加到应用管理中
checkCurrentAppId() {
let { vk } = this;
let systemInfo = uni.getSystemInfoSync();
let isHome = `/${this.appOptions.path}` === config.index.url; // 是否是首页
if (systemInfo.appId && isHome && vk.checkToken() && this.$hasRole('admin')) {
vk.callFunction({
url: 'admin/system/app/sys/getInfo',
data: {
appid: systemInfo.appId,
},
success: (data) => {
if (!data.info || data.info.appid !== systemInfo.appId) {
vk.confirm(`您当前登录的应用【${systemInfo.appId}】未在已有应用列表中,是否需要去添加?`, '提示', '前往应用管理', '取消', (res) => {
if (res.confirm) {
vk.navigateTo('/pages_plugs/system/app/list');
}
});
}
},
});
}
},
navigateToLogin() {
// 在app.vue中必须这样获取页面地址和参数才是正确的
let { vk, appOptions = {} } = this;
let params = vk.pubfn.queryParams(appOptions.query);
let url = `/${appOptions.path}${params}`;
let uniIdRedirectUrl = encodeURIComponent(url);
vk.reLaunch(`${config.login.url}?uniIdRedirectUrl=${uniIdRedirectUrl}`);
},
// 页面跳转拦截器
pageBeforeInterceptor() {
const invoke = (args = {}) => {
if (!this.vk.checkCurrentPagePermission({ url: args.url })) {
// 跳403页面
this.vk.navigateTo403();
return false;
}
};
uni.addInterceptor('redirectTo', { invoke });
uni.addInterceptor('navigateTo', { invoke });
uni.addInterceptor('reLaunch', { invoke });
},
},
// 监听 - 页面404
onPageNotFound(e) {
uni.redirectTo({
url: config.error.url,
});
},
// 监听 - 应用启动时
onLaunch: function (options) {
this.appOptions = options;
// 接收页面token参数,用于实现自动登录
this.vk.handleAutoLoginToken(options);
if (config.debug) {
console.log(
`%c vk-admin %c v${version} `,
'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px; color: #fff',
'background:#007aff ;padding: 1px; border-radius: 0 3px 3px 0; color: #fff; font-weight: bold;'
);
console.log('App Launch');
}
this.vk.pubfn.needInit({
that: this,
config,
success: () => {
this.init();
},
});
this.initApp();
this.pageBeforeInterceptor();
},
onShow: function () {
if (config.debug) console.log('App Show');
},
onHide: function () {
if (config.debug) console.log('App Hide');
},
};
</script>
<style lang="scss">
/**
* 每个页面公共css
* 特别注意,如果启动时报scss相关的错误,请卸载你的scss插件,重新安装
* 安装地址:https://ext.dcloud.net.cn/plugin?id=2046
*/
/* 此为uni-admin的样式,如果你不使用uni-admin的官方插件,可以不需要加载这些样式 */
@import '@/common/uni-admin/css/uni.css';
@import '@/common/uni-admin/css/uni-icons.css';
/* 此为uni-admin的样式,如果你不使用uni-admin的官方插件,可以不需要加载这些样式 */
</style>

205
app.config.js Normal file
View File

@@ -0,0 +1,205 @@
// 引入自定义公共函数
import myPubFunction from '@/common/function/myPubFunction.js';
// 引入静态菜单
import staticMenu from '@/app.config.menu.js';
// 引入主题配置文件
import themeConfig from '@/common/theme/index.js';
export default {
// 开发模式启用调式模式(请求时会打印日志)
debug: process.env.NODE_ENV !== 'production',
// 主云函数名称
functionName: 'router',
// 登录页面路径
login: {
url: '/pages/login/index',
// 显示在login页面的测试用户列表
testUser: {
show: false, // 是否显示
list: [
{ username: 'test11', password: '123456', nickname: '高级管理员' },
{ username: 'test12', password: '123456', nickname: '初级管理员' },
{ username: 'test13', password: '123456', nickname: '无权限用户' },
],
},
},
// 首页页面路径
index: {
url: '/pages/index/index',
},
// 404 Not Found 错误页面路径
error: {
url: '/pages_plugs/error/404',
},
// 403 无权限页面路径
noPermission: {
url: '/pages_plugs/error/403',
},
// 前端默认时区(中国为8)
targetTimezone: 8,
// 日志风格(注意:只在开发调试模式下生效,正式发布后不会打印日志)
logger: {
mode: 0, // 0 正常(默认展开) 1 简洁(默认折叠)
colorArr: ['#0095f8', '#67C23A'],
},
globalError: true, // 是否开启全局错误提示
// 需要检查是否登录的页面列表
// let { mode, list } = config.checkTokenPages
checkTokenPages: {
/**
* 如果 mode = 0 则代表自动检测
* 如果 mode = 1 则代表list内的页面需要登录,不在list内的页面不需要登录
* 如果 mode = 2 则代表list内的页面不需要登录,不在list内的页面需要登录
* 注意1: list内是通配符表达式,非正则表达式
* 在无需登录的页面上执行kh或sys函数,也会自动判断是否登录,未登录会自动跳登录页面,登录成功后会自动返回本来要跳转的页面。
*/
mode: 2,
list: ['/pages/login/*', '/pages_plugs/error/*', '/pages_template/element/*', '/pages_template/components/form/*', '/pages_template/components/icons/*'],
},
// 需要检查菜单权限的页面列表
checkPermissionPages: {
/**
* 如果 mode = 1 则代表list内的页面需要检查菜单权限,不在list内的页面不需要检查
* 如果 mode = 2 则代表list内的页面不需要检查菜单权限,不在list内的页面需要检查
* 注意: list内是通配符表达式,非正则表达式,如果一个页面不在左侧菜单中,但需要能访问,则需要在此设置
*/
mode: 2,
list: ['/pages/index/index', '/pages/login/*', '/pages_plugs/error/*'],
},
// 需要检查是否哪些请求需要加密通信
checkEncryptRequest: {
/**
* 如果 mode = 0 则不做处理
* 如果 mode = 1 则代表list内的云函数或云对象需要加密通信,不在list内的不需要加密通信
* 注意1: list内是正则表达式,非通配符表达式
* 注意2: 建议与 router/middleware/modules/encryptFilter.js 内的regExp保持一致
*/
mode: 1,
list: [
'^template/encrypt/(.*)', // 表示以 template/encrypt/ 开头的云函数或云对象需要加密通信
],
},
// 静态文件的资源URL地址
staticUrl: {
// 顶部导航
navBar: {
// 正方形 Logo 160*160
logo: '/static/logo.png',
// 长方形 Logo 224*160
logo1: '/static/logo1.png',
// 横幅 Logo 480*100
logo2: '/static/logo2.png',
},
},
// 自定义公共函数,myPubFunction内的函数可通过vk.myfn.xxx() 调用
myfn: myPubFunction,
// 第三方服务配置
service: {
// 云储存相关配置
cloudStorage: {
/**
* vk.uploadFile 接口默认使用哪个存储
* unicloud 空间内置存储(默认)
* extStorage 扩展存储
* aliyun 阿里云oss
*/
defaultProvider: 'unicloud',
// 空间内置存储
unicloud: {
// 暂无配置项
},
// 扩展存储配置
extStorage: {
provider: 'qiniu', // qiniu: 扩展存储-七牛云
// 根目录名称(如果改了这里的dirname,则云函数user/pub/getUploadFileOptionsForExtStorage内判断的目录权限也要改,否则无法上传)
dirname: 'public',
// 用于鉴权的云函数地址(一般不需要改这个参数)
authAction: 'user/pub/getUploadFileOptionsForExtStorage',
// 自定义域名,如:cdn.example.com(填你在扩展存储绑定的域名,若云端已设置 uni-config-center/vk-unicloud/index.js 内的 vk.service.cloudStorage.extStorage.domain 则此处可不填)
domain: '',
// 上传时,是否按用户id进行分组储存
groupUserId: true,
},
// 阿里云oss
// 密钥和签名信息(由于签名的获取比较麻烦,建议初学者使用上传到unicloud或extStorage的方案,上传到阿里云OSS是给有特殊需求的用户使用)
// 相关文档 : https://help.aliyun.com/document_detail/31925.html?spm=a2c4g.11186623.6.1757.b7987d9czoFCVu
aliyun: {
// 密钥和签名信息
uploadData: {
OSSAccessKeyId: '',
policy: '',
signature: '',
},
// oss上传地址
action: 'https://xxx.oss-cn-hangzhou.aliyuncs.com',
// 根目录名称
dirname: 'public',
// oss外网访问地址,也可以是阿里云cdn地址
host: 'https://xxx.xxx.com',
// 上传时,是否按用户id进行分组储存
groupUserId: true,
},
},
},
// 全局异常码,可以自定义提示结果
globalErrorCode: {
// 阿里云10秒非正常超时,其实请求还在执行(且一般已经成功了,但前端接受不到成功结果)
'cloudfunction-unusual-timeout': '请求超时,但请求还在执行,请重新进入页面。',
// 请求超时(真正的请求超时)
'cloudfunction-timeout': '请求超时,请重试!',
// 不在预期内的异常(如数据库异常、云函数编译异常等)
'cloudfunction-system-error': '网络开小差了!',
// 云函数达到并发限制
'cloudfunction-reaches-burst-limit': '系统繁忙,请稍后再试。',
// APP未授权网络访问时的异常提示
'cloudfunction-network-unauthorized': '需要进行网络请求许可,若您已授权,请点击确定',
},
// 页面风格
pageStyle: {
// 表单组件和表格组件的size,设为auto时,屏幕像素大于1600,则使用medium,否则使用small,且全局变量 `this.$global.size` 可获得实际生效的值
size: 'auto', // medium / small / mini / auto
},
// 左侧菜单
sideBar: {
// 配置静态菜单列表
staticMenu: staticMenu,
defaultOpeneds: ['components-admin'], // 默认展开的菜单项的 menu_id
navigateMode: 'redirectTo', // 默认 redirectTo,可选项为 navigateTo
},
// 顶部菜单
topBar: {
// 是否启用 menuTabs
showMenuTabs: true,
// logo显示模式,1 纯图片模式 2 图片+文字模式
logoMode: 1,
// logo标题
logoTitle: 'vk-admin后台管理',
},
// 主题配置
theme: {
// 当前使用哪个主题
use: 'blackWhite', // white blackWhite black custom
...themeConfig,
// 自定义主题
custom: {
// 左侧菜单样式
leftMenu: {
backgroundColor: '',
subBackgroundColor: '',
textColor: '',
activeTextColor: '',
activeBackgroundColor: '',
collapseActiveTextColor: '',
collapseActiveBackgroundColor: '',
hoverTextColor: '',
hoverBackgroundColor: '',
boxShadow: '',
borderTop: '',
},
// 顶部菜单样式
topMenu: {
backgroundColor: '',
textColor: '',
},
},
},
};

13
app.config.menu.js Normal file
View File

@@ -0,0 +1,13 @@
const debug = process.env.NODE_ENV !== 'production';
// 开发环境和正式环境中都会显示的静态菜单
import menus from './static_menu/menu.json';
// 只在开发环境中显示的静态菜单
import devMenus from './static_menu/menu-dev.json';
var menusList = [];
if (debug && devMenus) {
menusList = menus.concat(devMenus);
} else {
menusList = menus;
}
export default menusList;

2193
changelog.md Normal file

File diff suppressed because it is too large Load Diff

16
changelog.new.md Normal file
View File

@@ -0,0 +1,16 @@
## 1.25.0(2026-06-17)
- 【升级】`vk-unicloud-admin-ui` 的 npm 包(执行`npm i vk-unicloud-admin-ui@1.25.0 --registry https://registry.npmjs.org/`)
- 【升级】`vk依赖` [详情](https://ext.dcloud.net.cn/plugin?id=4157&update_log)
- 【重要】新增 `vk 统计` 模块,包含用户统计、支付统计、错误统计三大功能区,可通过菜单管理页「初始化vk统计」按钮一键导入菜单
- 【新增】`用户统计` 页面(概览 + 登录日志),展示新增用户趋势图、登录用户趋势图,支持按时/日/月维度切换
- 【新增】`支付统计` 页面(概览),展示收入趋势图、支付漏斗、支付方式占比饼图、按平台/订单类型收入明细表,支持日期快捷筛选和多维度筛选
- 【新增】`用户价值排行` 页面,支持按净收入/支付金额/支付笔数等多维度排序,支持导出 Excel,点击可跳转查看该用户的订单明细
- 【新增】`vk-stat-result` 统计结果表
- 【新增】定时任务 `statUser`(每小时执行用户统计)、`clearExpiredCache`(每小时清理 vk-global-data 过期缓存)
- 【调整】`支付订单明细` 页面,订单列表和详情弹窗从 `pages_plugs/system_uni/vk-pay-orders` 迁移至 `pages/vk-stats/pay`,详情弹窗 UI 全面重构(顶部概要卡片 + 基础信息/通知回调/退款信息三 Tab + 订单时间轴)
- 【调整】 `云端错误统计` 页面,从 `pages_plugs/system_uni/vk-error-log` 迁移至 `pages/vk-stats/error`,支持按状态筛选和处理操作
- 【调整】`登录日志` 页面,从 `pages_plugs/system_uni/uni-id-log` 迁移至 `pages/vk-stats/user`,支持按应用、IP、设备、用户等多维度筛选
- 【调整】云函数定时任务超时时间从 900 秒(15 分钟)提升至 7200 秒(2 小时)
##### 框架更新步骤 [点击查看](https://vkdoc.fsq.pub/admin/1/update.html)