feat: 添加通知公告功能和相关页面

refactor: 重构财务类型选项为独立变量

docs: 更新版本记录规则和文档

feat(workbench): 新增通知公告列表和详情页面

feat(h5office): 添加h5office文档预览组件和相关配置

style: 优化文章页面样式和布局

fix(update): 添加版本检查日志输出
This commit is contained in:
砂糖
2025-11-07 10:30:50 +08:00
parent 307b46b213
commit 884b1bb311
17 changed files with 2175 additions and 33 deletions

View File

@@ -1,22 +1,220 @@
<template>
<view>
<view class="article-container">
<!-- 加载状态 -->
<view class="loading" v-if="loading">
<uni-loading-icon size="24" color="#007aff"></uni-loading-icon>
<text class="loading-text">加载中...</text>
</view>
<!-- 文章内容加载完成后显示 -->
<view class="article-content" v-else-if="article">
<!-- 帖子标题 -->
<view class="post-title">{{ article.title || '无标题' }}</view>
<!-- 用户信息 -->
<view class="user-info">
<image v-if="article.avatar" class="user-avatar" :src="article.avatar || '/static/default-avatar.png'" mode="cover"></image>
<view class="user-meta">
<view class="user-name">
{{ article.author || '未知用户' }}
<!-- <view class="user-level">Lv.1</view> -->
</view>
<view class="user-time-area">
<text>{{ article.createTime || '未知时间' }}</text>
<text v-if="article.area"> · {{ article.area || '未知地区' }}</text>
</view>
</view>
<!-- <view class="follow-btn">+关注</view> -->
</view>
<!-- 富文本内容 -->
<mp-html :content="article.content"></mp-html>
</view>
<!-- 错误状态 -->
<view class="error" v-else>
<uni-icons type="warn" size="36" color="#ff4d4f"></uni-icons>
<text class="error-text">加载失败</text>
<button class="retry-btn" @click="reload">重试</button>
</view>
</view>
</template>
<script>
export default {
import { getNotice } from '@/api/oa/notice';
export default {
data() {
return {
article: null,
loading: true,
};
},
onLoad(options) {
const { type, id } = options;
if (!type || !id) {
uni.showToast({ title: '参数错误', icon: 'none' });
setTimeout(() => uni.navigateBack(), 1000);
return;
}
this.fetchData(type, id);
},
methods: {
fetchData(type, id) {
this.loading = true;
switch (type) {
case 'notice':
getNotice(id)
.then(res => {
this.article = {
...res.data,
author: res.data.createBy,
title: res.data.noticeTitle,
content: res.data.noticeContent,
};
this.loading = false;
})
.catch(err => {
console.error('获取文章失败', err);
this.article = null;
this.loading = false;
uni.showToast({ title: '加载失败', icon: 'none' });
});
break;
default:
uni.showToast({ title: '未知类型', icon: 'none' });
setTimeout(() => uni.navigateBack(), 1000);
break;
}
},
reload() {
const { type, id } = this.$options.onLoad.options;
this.fetchData(type, id);
},
navigateBack() {
uni.navigateBack();
}
}
};
</script>
<style>
<style scoped>
.article-container {
min-height: 100vh;
background-color: #fff;
}
.loading {
display: flex;
flex-direction: column;
align-items: center;
padding: 50px 0;
}
.loading-text {
margin-top: 10px;
color: #666;
font-size: 14px;
}
.error {
display: flex;
flex-direction: column;
align-items: center;
padding: 50px 0;
}
.error-text {
margin: 15px 0;
color: #ff4d4f;
font-size: 16px;
}
.retry-btn {
background-color: #007aff;
color: #fff;
border-radius: 4px;
padding: 8px 20px;
font-size: 14px;
}
.article-content {
padding: 16px;
}
.post-title {
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 12px;
line-height: 1.4;
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.user-meta {
flex: 1;
}
.user-name {
display: flex;
align-items: center;
font-size: 14px;
color: #333;
margin-bottom: 4px;
}
.user-level {
background-color: #dff0d8;
color: #5cb85c;
font-size: 10px;
padding: 1px 4px;
border-radius: 3px;
margin-left: 6px;
}
.user-time-area {
font-size: 12px;
color: #999;
}
.follow-btn {
font-size: 12px;
color: #5cb85c;
background-color: #e8f5e9;
padding: 4px 8px;
border-radius: 12px;
}
.mp-html {
font-size: 15px;
color: #333;
line-height: 1.8;
}
.mp-html img {
max-width: 100%;
height: auto;
margin: 10px 0;
border-radius: 8px;
}
.interact-area {
display: flex;
justify-content: space-around;
margin-top: 20px;
padding: 10px 0;
border-top: 1px solid #eee;
}
.interact-item {
display: flex;
flex-direction: column;
align-items: center;
font-size: 12px;
color: #999;
}
.interact-count {
margin-top: 4px;
}
</style>

View File

@@ -109,6 +109,12 @@ export default {
url: '/pages/workbench/customer/customer',
category: '信息中心'
},
{
text: '通知公告',
icon: '/static/images/notice.png',
url: '/pages/workbench/notice/notice',
category: '信息中心'
},
{
text: '库存盘点',
icon: '/static/images/stock.png',

View File

@@ -1,22 +1,98 @@
<template>
<view>
<!-- 通知公告列表展示noticeTitle createBy, createTime -->
<div class="notice-list-container">
<!-- 判空提示 -->
<div class="empty-tip" v-if="notices.length === 0">
暂无通知公告
</div>
</view>
<!-- 公告列表 -->
<div class="notice-list" v-else>
<div class="notice-item" v-for="(notice, index) in notices" :key="index" @click="toDetail(notice.noticeId)">
<div class="notice-title">{{ notice.noticeTitle }}</div>
<div class="notice-meta">
<span class="create-by">{{ notice.createBy || '未知发布人' }}</span>
<span class="create-time">{{ notice.createTime || '未知时间' }}</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
import { listNotice } from '@/api/oa/notice.js'
export default {
data() {
return {
notices: []
}
},
mounted() {
listNotice({ pageSize: 999, pageNum: 1 }).then(res => {
// 确保res.rows存在避免空数据报错
this.notices = res?.rows || [];
}).catch(err => {
console.error('获取通知公告失败', err);
this.notices = [];
});
},
methods: {
toDetail(id) {
uni.navigateTo({
url: '/pages/workbench/article/article?type=notice&id=' + id
})
}
}
}
</script>
<style>
<style scoped lang="scss">
.notice-list-container {
background-color: #f5f5f5; // 米色背景
padding: 15px;
min-height: 100vh; // 避免容器过矮
.empty-tip {
text-align: center;
padding: 30px 0;
color: #999;
font-size: 14px;
}
.notice-list {
.notice-item {
background-color: #fff; // 白色公告背景
padding: 12px 15px;
margin-bottom: 10px;
border-radius: 6px;
box-shadow: 0 1px 2px rgba(0,0,0,0.05); // 轻微阴影增强层次感
.notice-title {
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis; // 标题过长省略
}
.notice-meta {
display: flex;
justify-content: space-between; // 发布人和时间两端对齐
font-size: 13px;
color: #666;
.create-by {
&:after {
content: '|'; // 分隔符
margin: 0 8px;
color: #ccc;
}
}
}
}
}
}
</style>

View File

@@ -64,10 +64,7 @@
<uni-forms ref="financeForm" :model="form" :rules="rules" label-width="150rpx">
<uni-forms-item label="财务类型" name="financeType">
<uni-data-checkbox mode="tag" v-model="form.financeType" :localdata="[
{ value: "1", text: '出账' },
{ value: '2', text: '入账' }
]"></uni-data-checkbox>
<uni-data-checkbox mode="tag" v-model="form.financeType" :localdata="financeTypeOption"></uni-data-checkbox>
</uni-forms-item>
<uni-forms-item label="记录名称" name="financeTitle">
@@ -282,6 +279,10 @@ export default {
currentFinance: {
detailList: []
},
financeTypeOption: [
{ value: "1", text: '出账' },
{ value: '2', text: '入账' }
],
loading: false,
// 表单数据 - 默认包含一条空明细

BIN
static/images/notice.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,4 @@
## 1.0.12023-02-17
文档更新
## 1.0.02023-02-15
版本上架

View File

@@ -0,0 +1,142 @@
<template>
<view class="appGetFile" id="appGetFile"></view>
</template>
<script>
import { BASE_URL } from './config.js'
import { aesEncrypt } from './tool.js'
var wv //计划创建的webview
let inter
export default {
props: {
appid: {
type: String
},
currentWebview: {
type: Object
},
btnStyle: {
type: String,
default: 'font-size: 14px;background-color: aqua;'
},
content: {
type: String,
default: '<button>按钮</button>'
},
isShow: {
type: Boolean,
default: true
}
},
watch: {
appid: {
handler() {
this.appidKey = aesEncrypt(this.appid)
},
immediate: true
},
isShow: {
handler(newVal) {
wv.setVisible(newVal)
}
}
},
mounted() {
// #ifdef APP-PLUS
const params = {
btnStyle: this.btnStyle,
content: this.content
}
this.$nextTick(() => {
wv = plus.webview.create('', 'custom-webview', {
cachemode: 'noCache'
})
plus.globalEvent.addEventListener('plusMessage', e => {
if (wv && wv['__uuid__'] === e.originId) {
this.childEvent(e.data.args.data.arg)
}
})
wv.loadURL(this.BASE_URL + this.API + '?params=' + encodeURIComponent(JSON.stringify(params)))
this.currentWebview.append(wv)
this.setPosition()
setTimeout(() => {
wv.evalJS("getAppidKey({type:'event',appidKey:'" + this.appidKey + "'})")
}, 500)
})
// #endif
},
data() {
return {
BASE_URL: BASE_URL,
API: '/appWebViewUploadFile',
appidKey: '',
top: 100
}
},
methods: {
// appGetFileClick() {
// wv.evalJS("onGetFile({type:'event',appidKey:'" + this.appidKey + "'})")
// },
childEvent(data) {
if (data.type && data.type === 'webOffice') {
switch (data.event) {
case 'error':
//this.showErrorMsg(data.msg)
this.$emit('error', data.msg)
break
case 'success':
this.$emit('success', data.msg)
break
case 'readFile':
this.$emit('readFile', data.msg)
break
case 'complete':
this.$emit('complete', data.msg)
break
}
}
},
showErrorMsg(msg) {
uni.showModal({
title: '系统异常!',
content: msg,
showCancel: false,
success: () => {
//this.closePre()
}
})
},
setPosition() {
inter && clearInterval(inter)
const h = uni.getSystemInfoSync().screenHeight - uni.getSystemInfoSync().windowHeight
inter = setInterval(() => {
const query = uni.createSelectorQuery()
query.select('#appGetFile').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec(res => {
const dome = res[0]
wv.setStyle({
top: h + dome.top,
left: dome.left,
height: dome.height,
width: dome.width
})
})
}, 15)
}
}
}
</script>
<style>
.appGetFile {
width: 200px;
height: 80px;
}
</style>

View File

@@ -0,0 +1,19 @@
/* 支持的文件 */
export const FILE_TYPES = ['csv', 'djvu', 'doc', 'docm', 'docx', 'docxf', 'dot', 'dotm', 'dotx', 'epub', 'fb2', 'fodp',
'fods', 'fodt', 'htm', 'html', 'mht', 'odp', 'ods', 'odt', 'oform', 'otp', 'ots', 'ott', 'oxps', 'pdf', 'pot',
'potm', 'potx', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', 'rtf', 'txt', 'xls', 'xlsb', 'xlsm', 'xlsx',
'xlt',
'xltm', 'xltx', 'xml', 'xps'
]
/* */
export const BASE_URL = 'https://tool.h5office.cn/index.php'
export const AES_KEY = "68d7eec0ba694a68"
export const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmkANmC849IOntYQQdSgLvMMGm
8V/u838ATHaoZwvweoYyd+/7Wx+bx5bdktJb46YbqS1vz3VRdXsyJIWhpNcmtKhY
inwcl83aLtzJeKsznppqMyAIseaKIeAm6tT8uttNkr2zOymL/PbMpByTQeEFlyy1
poLBwrol0F4USc+owwIDAQAB
-----END PUBLIC KEY-----`

View File

@@ -0,0 +1,753 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.browserMD5File = factory());
}(this, (function () { 'use strict';
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var sparkMd5 = createCommonjsModule(function (module, exports) {
(function (factory) {
{
// Node/CommonJS
module.exports = factory();
}
}(function (undefined) {
/*
* Fastest md5 implementation around (JKM md5).
* Credits: Joseph Myers
*
* @see http://www.myersdaily.org/joseph/javascript/md5-text.html
* @see http://jsperf.com/md5-shootout/7
*/
/* this function is much faster,
so if possible we use it. Some IEs
are the only ones I know of that
need the idiotic second function,
generated by an if clause. */
var add32 = function (a, b) {
return (a + b) & 0xFFFFFFFF;
},
hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
function cmn(q, a, b, x, s, t) {
a = add32(add32(a, q), add32(x, t));
return add32((a << s) | (a >>> (32 - s)), b);
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t) {
return cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t) {
return cmn(b ^ c ^ d, a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t) {
return cmn(c ^ (b | (~d)), a, b, x, s, t);
}
function md5cycle(x, k) {
var a = x[0],
b = x[1],
c = x[2],
d = x[3];
a = ff(a, b, c, d, k[0], 7, -680876936);
d = ff(d, a, b, c, k[1], 12, -389564586);
c = ff(c, d, a, b, k[2], 17, 606105819);
b = ff(b, c, d, a, k[3], 22, -1044525330);
a = ff(a, b, c, d, k[4], 7, -176418897);
d = ff(d, a, b, c, k[5], 12, 1200080426);
c = ff(c, d, a, b, k[6], 17, -1473231341);
b = ff(b, c, d, a, k[7], 22, -45705983);
a = ff(a, b, c, d, k[8], 7, 1770035416);
d = ff(d, a, b, c, k[9], 12, -1958414417);
c = ff(c, d, a, b, k[10], 17, -42063);
b = ff(b, c, d, a, k[11], 22, -1990404162);
a = ff(a, b, c, d, k[12], 7, 1804603682);
d = ff(d, a, b, c, k[13], 12, -40341101);
c = ff(c, d, a, b, k[14], 17, -1502002290);
b = ff(b, c, d, a, k[15], 22, 1236535329);
a = gg(a, b, c, d, k[1], 5, -165796510);
d = gg(d, a, b, c, k[6], 9, -1069501632);
c = gg(c, d, a, b, k[11], 14, 643717713);
b = gg(b, c, d, a, k[0], 20, -373897302);
a = gg(a, b, c, d, k[5], 5, -701558691);
d = gg(d, a, b, c, k[10], 9, 38016083);
c = gg(c, d, a, b, k[15], 14, -660478335);
b = gg(b, c, d, a, k[4], 20, -405537848);
a = gg(a, b, c, d, k[9], 5, 568446438);
d = gg(d, a, b, c, k[14], 9, -1019803690);
c = gg(c, d, a, b, k[3], 14, -187363961);
b = gg(b, c, d, a, k[8], 20, 1163531501);
a = gg(a, b, c, d, k[13], 5, -1444681467);
d = gg(d, a, b, c, k[2], 9, -51403784);
c = gg(c, d, a, b, k[7], 14, 1735328473);
b = gg(b, c, d, a, k[12], 20, -1926607734);
a = hh(a, b, c, d, k[5], 4, -378558);
d = hh(d, a, b, c, k[8], 11, -2022574463);
c = hh(c, d, a, b, k[11], 16, 1839030562);
b = hh(b, c, d, a, k[14], 23, -35309556);
a = hh(a, b, c, d, k[1], 4, -1530992060);
d = hh(d, a, b, c, k[4], 11, 1272893353);
c = hh(c, d, a, b, k[7], 16, -155497632);
b = hh(b, c, d, a, k[10], 23, -1094730640);
a = hh(a, b, c, d, k[13], 4, 681279174);
d = hh(d, a, b, c, k[0], 11, -358537222);
c = hh(c, d, a, b, k[3], 16, -722521979);
b = hh(b, c, d, a, k[6], 23, 76029189);
a = hh(a, b, c, d, k[9], 4, -640364487);
d = hh(d, a, b, c, k[12], 11, -421815835);
c = hh(c, d, a, b, k[15], 16, 530742520);
b = hh(b, c, d, a, k[2], 23, -995338651);
a = ii(a, b, c, d, k[0], 6, -198630844);
d = ii(d, a, b, c, k[7], 10, 1126891415);
c = ii(c, d, a, b, k[14], 15, -1416354905);
b = ii(b, c, d, a, k[5], 21, -57434055);
a = ii(a, b, c, d, k[12], 6, 1700485571);
d = ii(d, a, b, c, k[3], 10, -1894986606);
c = ii(c, d, a, b, k[10], 15, -1051523);
b = ii(b, c, d, a, k[1], 21, -2054922799);
a = ii(a, b, c, d, k[8], 6, 1873313359);
d = ii(d, a, b, c, k[15], 10, -30611744);
c = ii(c, d, a, b, k[6], 15, -1560198380);
b = ii(b, c, d, a, k[13], 21, 1309151649);
a = ii(a, b, c, d, k[4], 6, -145523070);
d = ii(d, a, b, c, k[11], 10, -1120210379);
c = ii(c, d, a, b, k[2], 15, 718787259);
b = ii(b, c, d, a, k[9], 21, -343485551);
x[0] = add32(a, x[0]);
x[1] = add32(b, x[1]);
x[2] = add32(c, x[2]);
x[3] = add32(d, x[3]);
}
function md5blk(s) {
var md5blks = [],
i; /* Andy King said do it this way. */
for (i = 0; i < 64; i += 4) {
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
}
return md5blks;
}
function md5blk_array(a) {
var md5blks = [],
i; /* Andy King said do it this way. */
for (i = 0; i < 64; i += 4) {
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
}
return md5blks;
}
function md51(s) {
var n = s.length,
state = [1732584193, -271733879, -1732584194, 271733878],
i,
length,
tail,
tmp,
lo,
hi;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk(s.substring(i - 64, i)));
}
s = s.substring(i - 64);
length = s.length;
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
}
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Beware that the final length might not fit in 32 bits so we take care of that
tmp = n * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(state, tail);
return state;
}
function md51_array(a) {
var n = a.length,
state = [1732584193, -271733879, -1732584194, 271733878],
i,
length,
tail,
tmp,
lo,
hi;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
}
// Not sure if it is a bug, however IE10 will always produce a sub array of length 1
// containing the last element of the parent array if the sub array specified starts
// beyond the length of the parent array - weird.
// https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
length = a.length;
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= a[i] << ((i % 4) << 3);
}
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Beware that the final length might not fit in 32 bits so we take care of that
tmp = n * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(state, tail);
return state;
}
function rhex(n) {
var s = '',
j;
for (j = 0; j < 4; j += 1) {
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
}
return s;
}
function hex(x) {
var i;
for (i = 0; i < x.length; i += 1) {
x[i] = rhex(x[i]);
}
return x.join('');
}
// In some cases the fast add32 function cannot be used..
if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {
add32 = function (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
};
}
// ---------------------------------------------------
/**
* ArrayBuffer slice polyfill.
*
* @see https://github.com/ttaubert/node-arraybuffer-slice
*/
if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
(function () {
function clamp(val, length) {
val = (val | 0) || 0;
if (val < 0) {
return Math.max(val + length, 0);
}
return Math.min(val, length);
}
ArrayBuffer.prototype.slice = function (from, to) {
var length = this.byteLength,
begin = clamp(from, length),
end = length,
num,
target,
targetArray,
sourceArray;
if (to !== undefined) {
end = clamp(to, length);
}
if (begin > end) {
return new ArrayBuffer(0);
}
num = end - begin;
target = new ArrayBuffer(num);
targetArray = new Uint8Array(target);
sourceArray = new Uint8Array(this, begin, num);
targetArray.set(sourceArray);
return target;
};
})();
}
// ---------------------------------------------------
/**
* Helpers.
*/
function toUtf8(str) {
if (/[\u0080-\uFFFF]/.test(str)) {
str = unescape(encodeURIComponent(str));
}
return str;
}
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
var length = str.length,
buff = new ArrayBuffer(length),
arr = new Uint8Array(buff),
i;
for (i = 0; i < length; i += 1) {
arr[i] = str.charCodeAt(i);
}
return returnUInt8Array ? arr : buff;
}
function arrayBuffer2Utf8Str(buff) {
return String.fromCharCode.apply(null, new Uint8Array(buff));
}
function concatenateArrayBuffers(first, second, returnUInt8Array) {
var result = new Uint8Array(first.byteLength + second.byteLength);
result.set(new Uint8Array(first));
result.set(new Uint8Array(second), first.byteLength);
return returnUInt8Array ? result : result.buffer;
}
function hexToBinaryString(hex) {
var bytes = [],
length = hex.length,
x;
for (x = 0; x < length - 1; x += 2) {
bytes.push(parseInt(hex.substr(x, 2), 16));
}
return String.fromCharCode.apply(String, bytes);
}
// ---------------------------------------------------
/**
* SparkMD5 OOP implementation.
*
* Use this class to perform an incremental md5, otherwise use the
* static methods instead.
*/
function SparkMD5() {
// call reset to init the instance
this.reset();
}
/**
* Appends a string.
* A conversion will be applied if an utf8 string is detected.
*
* @param {String} str The string to be appended
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.append = function (str) {
// Converts the string to utf8 bytes if necessary
// Then append as binary
this.appendBinary(toUtf8(str));
return this;
};
/**
* Appends a binary string.
*
* @param {String} contents The binary string to be appended
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.appendBinary = function (contents) {
this._buff += contents;
this._length += contents.length;
var length = this._buff.length,
i;
for (i = 64; i <= length; i += 64) {
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
}
this._buff = this._buff.substring(i - 64);
return this;
};
/**
* Finishes the incremental computation, reseting the internal state and
* returning the result.
*
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.prototype.end = function (raw) {
var buff = this._buff,
length = buff.length,
i,
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
ret;
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
}
this._finish(tail, length);
ret = hex(this._hash);
if (raw) {
ret = hexToBinaryString(ret);
}
this.reset();
return ret;
};
/**
* Resets the internal state of the computation.
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.reset = function () {
this._buff = '';
this._length = 0;
this._hash = [1732584193, -271733879, -1732584194, 271733878];
return this;
};
/**
* Gets the internal state of the computation.
*
* @return {Object} The state
*/
SparkMD5.prototype.getState = function () {
return {
buff: this._buff,
length: this._length,
hash: this._hash
};
};
/**
* Gets the internal state of the computation.
*
* @param {Object} state The state
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.setState = function (state) {
this._buff = state.buff;
this._length = state.length;
this._hash = state.hash;
return this;
};
/**
* Releases memory used by the incremental buffer and other additional
* resources. If you plan to use the instance again, use reset instead.
*/
SparkMD5.prototype.destroy = function () {
delete this._hash;
delete this._buff;
delete this._length;
};
/**
* Finish the final calculation based on the tail.
*
* @param {Array} tail The tail (will be modified)
* @param {Number} length The length of the remaining buffer
*/
SparkMD5.prototype._finish = function (tail, length) {
var i = length,
tmp,
lo,
hi;
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(this._hash, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Do the final computation based on the tail and length
// Beware that the final length may not fit in 32 bits so we take care of that
tmp = this._length * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(this._hash, tail);
};
/**
* Performs the md5 hash on a string.
* A conversion will be applied if utf8 string is detected.
*
* @param {String} str The string
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.hash = function (str, raw) {
// Converts the string to utf8 bytes if necessary
// Then compute it using the binary function
return SparkMD5.hashBinary(toUtf8(str), raw);
};
/**
* Performs the md5 hash on a binary string.
*
* @param {String} content The binary string
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.hashBinary = function (content, raw) {
var hash = md51(content),
ret = hex(hash);
return raw ? hexToBinaryString(ret) : ret;
};
// ---------------------------------------------------
/**
* SparkMD5 OOP implementation for array buffers.
*
* Use this class to perform an incremental md5 ONLY for array buffers.
*/
SparkMD5.ArrayBuffer = function () {
// call reset to init the instance
this.reset();
};
/**
* Appends an array buffer.
*
* @param {ArrayBuffer} arr The array to be appended
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.append = function (arr) {
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
length = buff.length,
i;
this._length += arr.byteLength;
for (i = 64; i <= length; i += 64) {
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
}
this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
return this;
};
/**
* Finishes the incremental computation, reseting the internal state and
* returning the result.
*
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.ArrayBuffer.prototype.end = function (raw) {
var buff = this._buff,
length = buff.length,
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
i,
ret;
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= buff[i] << ((i % 4) << 3);
}
this._finish(tail, length);
ret = hex(this._hash);
if (raw) {
ret = hexToBinaryString(ret);
}
this.reset();
return ret;
};
/**
* Resets the internal state of the computation.
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.reset = function () {
this._buff = new Uint8Array(0);
this._length = 0;
this._hash = [1732584193, -271733879, -1732584194, 271733878];
return this;
};
/**
* Gets the internal state of the computation.
*
* @return {Object} The state
*/
SparkMD5.ArrayBuffer.prototype.getState = function () {
var state = SparkMD5.prototype.getState.call(this);
// Convert buffer to a string
state.buff = arrayBuffer2Utf8Str(state.buff);
return state;
};
/**
* Gets the internal state of the computation.
*
* @param {Object} state The state
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.setState = function (state) {
// Convert string to buffer
state.buff = utf8Str2ArrayBuffer(state.buff, true);
return SparkMD5.prototype.setState.call(this, state);
};
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
/**
* Performs the md5 hash on an array buffer.
*
* @param {ArrayBuffer} arr The array buffer
* @param {Boolean} raw True to get the raw string, false to get the hex one
*
* @return {String} The result
*/
SparkMD5.ArrayBuffer.hash = function (arr, raw) {
var hash = md51_array(new Uint8Array(arr)),
ret = hex(hash);
return raw ? hexToBinaryString(ret) : ret;
};
return SparkMD5;
}));
});
class BMF {
md5(file, md5Fn, progressFn) {
this.aborted = false;
this.progress = 0;
let currentChunk = 0;
const blobSlice =
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice;
const chunkSize = 2097152;
const chunks = Math.ceil(file.size / chunkSize);
const spark = new sparkMd5.ArrayBuffer();
const reader = new FileReader();
loadNext();
reader.onloadend = e => {
spark.append(e.target.result); // Append array buffer
currentChunk++;
this.progress = currentChunk / chunks;
if (progressFn && typeof progressFn === 'function') {
progressFn(this.progress);
}
if (this.aborted) {
md5Fn('aborted');
return
}
if (currentChunk < chunks) {
loadNext();
} else {
md5Fn(null, spark.end());
}
};
/////////////////////////
function loadNext() {
const start = currentChunk * chunkSize;
const end = start + chunkSize >= file.size ? file.size : start + chunkSize;
reader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
}
abort() {
this.aborted = true;
}
}
return BMF;
})));

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,219 @@
var hexcase = 0;
var b64pad = "";
var chrsz = 8;
export function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
function md5_vm_test()
{
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}
function core_md5(x, len)
{
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);
}
function md5_cmn(q, a, b, x, s, t)
{
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
function core_hmac_md5(key, data)
{
var bkey = str2binl(key);
if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
return core_md5(opad.concat(hash), 512 + 128);
}
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}
function str2binl(str)
{
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
return bin;
}
function binl2str(bin)
{
var str = "";
var mask = (1 << chrsz) - 1;
for(var i = 0; i < bin.length * 32; i += chrsz)
str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
return str;
}
function binl2hex(binarray)
{
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++)
{
str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
}
return str;
}
function binl2b64(binarray)
{
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var str = "";
for(var i = 0; i < binarray.length * 4; i += 3)
{
var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
}
}
return str;
}

View File

@@ -0,0 +1,405 @@
<template>
<view class="preview" v-if="isShow">
<template v-if="src">
<!-- #ifdef APP -->
<web-view :src="src" @message="webMess"></web-view>
<!-- #endif -->
<!-- #ifdef H5 -->
<iframe
id="if"
:src="src"
align="top"
frameborder="0"
name="frameEditor"
allowfullscreen=""
onmousewheel=""
allow="autoplay; camera; microphone; display-capture; clipboard-write;"
style="position: fixed; overflow: hidden;"
></iframe>
<!-- #endif -->
</template>
<!-- <template v-else>
{{ tps }}
</template> -->
</view>
</template>
<script>
import { FILE_TYPES, BASE_URL } from './config.js'
import { hex_md5 } from './md5.js'
import {
browserFileToMd5,
browserUploadFile,
request,
plus_io_getFileInfo,
plus_uploader,
aesEncrypt,
colorRGBtoHex
} from './tool.js'
export default {
model: {
prop: 'isShow',
event: 'change'
},
props: {
isShow: {
type: Boolean,
default: false
},
appid: {
type: String
},
// #ifdef APP-PLUS
file: {
type: [String],
default: ''
},
// #endif
// #ifdef H5
file: {
type: [String, File],
default: ''
},
// #endif
fileName: {
type: String,
default: ''
},
privilege: {
type: Array,
default: () => ['copy', 'download', 'print']
}
},
watch: {
isShow: {
async handler() {
this.src = ''
if (!this.isShow) return
if (!this.appid) return this.showErrorMsg('请填写appid')
this.$emit('create')
let params = {
fileUrl: '',
fileName: '',
fileExit: '',
digest: '',
type: '',
privilege: this.privilege,
appid: this.appidKey
}
if (typeof this.file === 'string') {
if (this.file.indexOf('http://') === 0 || this.file.indexOf('https://') === 0) {
// 走网络文件
const res = this.getHttpFileInfo(this.file, this.fileName)
if (typeof res === 'string') return this.showErrorMsg(res)
else
params = {
...params,
...res
}
} else if (this.file.indexOf('md5::') === 0) {
// app 外部上传
const res = await this.getAppFileInfoToMd5(this.file)
if (typeof res === 'string') return this.showErrorMsg(res)
else
params = {
...params,
...res
}
} else {
// 走 app 内部本地上传
try {
const res = await this.getAppFileInfo(this.file)
if (typeof res === 'string') return this.showErrorMsg(res)
else
params = {
...params,
...res
}
} catch (e) {
return this.showErrorMsg('本地文件异常 001:' + e.msg ?? e.message)
}
}
} else {
// 走浏览器本地上传
try {
if (this.file.lastModifiedDate) {
const res = await this.getInputFileInfo(this.file)
if (typeof res === 'string') return this.showErrorMsg(res)
else
params = {
...params,
...res
}
} else {
return this.showErrorMsg('本地文件异常 002')
}
} catch (e) {
return this.showErrorMsg('本地文件异常 001:' + e.msg ?? e.message)
}
}
this.src = this.paseUrl(params)
},
immediate: true
},
appid: {
handler() {
this.appidKey = aesEncrypt(this.appid)
},
immediate: true
},
src: {
handler(newVal) {
if (newVal) {
this.$emit('created')
}
}
}
},
mounted() {
// #ifdef H5
this.addH5Event()
// #endif
},
data() {
return {
src: '',
tps: '文件读取中...',
appidKey: ''
}
},
methods: {
closePre() {
this.$emit('change', false)
//this.$emit('update:openLocalFile', '')
this.src = ''
},
paseUrl(param) {
param['host'] = 'app'
// #ifdef H5
param['host'] = origin
// #endif
param = encodeURIComponent(JSON.stringify(param))
return BASE_URL + '?param=' + param
},
getHttpFileInfo(src, fileName) {
if (!fileName) {
let path = src.split('/')
if (!path.length) return '传入文件路径格式不正确或者文件名称后缀不正确'
fileName = path.pop()
}
let exit = fileName.split('.')
if (!exit.length) return '传入文件名称未添加后缀类型'
exit = exit.pop()
if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
return {
fileUrl: src,
fileName: fileName,
fileExit: exit,
digest: hex_md5(src),
type: 'src'
}
},
async getInputFileInfo(fileLocal) {
try {
const fileName = fileLocal.name
let exit = fileName.split('.')
if (!exit.length) return '传入文件名称未添加后缀类型'
exit = exit.pop()
if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
const fileMd5 = await browserFileToMd5(fileLocal)
const fileInfo = {
size: fileLocal.size,
fileMd5: fileMd5,
appid: this.appidKey
}
const checkRes = await request({
url: '/checkFileInfo',
type: 'POST',
data: fileInfo
})
if (checkRes.msg === 0) {
const res = await browserUploadFile('/uploadLocalFile', fileLocal, {
fileMd5: fileMd5,
appid: this.appidKey,
size: fileLocal.size,
fileExit: exit,
fileName: fileName + '(无缓存)',
host: origin
})
}
const fileUrl = 'http://localhost/' + fileMd5 + '.' + exit
return {
fileMd5: fileMd5,
fileUrl: fileUrl,
fileName: fileName,
fileExit: exit,
digest: hex_md5(fileUrl),
type: 'localhost'
}
} catch (e) {
throw e
}
},
async getAppFileInfo(fileLocalPath) {
try {
let { size, digest: fileMd5 } = await plus_io_getFileInfo(fileLocalPath)
fileMd5 = fileMd5.toLowerCase()
const fileName = fileLocalPath.split('/').pop()
let exit = fileName.split('.')
if (!exit.length) return '传入文件名称未添加后缀类型'
exit = exit.pop()
if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
const fileInfo = {
size: size,
fileMd5: fileMd5,
appid: this.appidKey
}
const checkRes = await request({
url: '/checkFileInfo',
type: 'POST',
data: fileInfo
})
if (checkRes.msg === 0) {
const res = await plus_uploader('/uploadLocalFile', fileLocalPath, {
fileMd5: fileMd5,
appid: this.appidKey,
size: size + '',
fileExit: exit,
fileName: fileName + '(无缓存)',
host: 'app'
})
}
const fileUrl = 'http://localhost/' + fileMd5 + '.' + exit
return {
fileMd5: fileMd5,
fileUrl: fileUrl,
fileName: fileName,
fileExit: exit,
digest: hex_md5(fileUrl),
type: 'localhost'
}
} catch (e) {
console.log(e, 'err')
throw e
}
},
getAppFileInfoToMd5(fileLocalPath) {
const fileName = fileLocalPath.replace('md5::', '')
let exit = fileName.split('.')
if (!exit.length) return '传入文件名称未添加后缀类型'
const fileMd5 = exit[0]
exit = exit.pop()
if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
const fileUrl = 'http://localhost/' + fileMd5 + '.' + exit
return {
fileMd5: fileMd5,
fileUrl: fileUrl,
fileName: fileName,
fileExit: exit,
digest: hex_md5(fileUrl),
type: 'localhost'
}
},
addH5Event() {
window.addEventListener(
'message',
e => {
if (e.data && typeof e.data === 'string') {
const data = JSON.parse(e.data)
this.childEvent(data)
}
},
false
)
},
webMess(e) {
this.childEvent(e.detail.data[0])
},
childEvent(data) {
if (data.type && data.type === 'webOffice') {
switch (data.event) {
case 'close':
this.closePre()
this.$emit('close')
break
case 'error':
this.showErrorMsg(data.msg)
break
case 'download':
this.$emit('download', data.msg)
break
case 'init':
let params = JSON.parse(data.msg)
params.backgroundColor = colorRGBtoHex(params.backgroundColor)
this.$emit('init', params)
break
case 'inited':
let paramss = JSON.parse(data.msg)
paramss.backgroundColor = colorRGBtoHex(paramss.backgroundColor)
this.$emit('inited', paramss)
break
case 'readFiled':
this.$emit('readFiled', data.msg)
break
}
}
},
showErrorMsg(msg) {
this.$emit('error', msg)
uni.showModal({
title: '系统异常!',
content: msg,
showCancel: false,
success: () => {
this.closePre()
}
})
}
}
}
</script>
<style lang="scss" scoped>
.preview {
position: fixed;
top: var(--window-top);
bottom: 0;
left: 0;
right: 0;
z-index: 100;
background-color: #fff;
.error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ccc;
}
.close {
position: absolute;
top: 5px;
right: 10px;
background-color: #ccc;
color: #fff;
padding: 5px 10px;
border-radius: 5px;
font-size: 12px;
z-index: 102;
}
#if {
position: relative;
z-index: 101;
width: 100%;
height: calc(100% - var(--window-top));
}
}
</style>

View File

@@ -0,0 +1,127 @@
import FileMd5 from './index.umd.js'
import {
BASE_URL,
AES_KEY as aa,
PUBLIC_KEY
} from './config.js'
import JSEncrypt from './jsencrypt.min.js'
import {
hex_md5
} from './md5.js'
/** 浏览器获取文件 md5 */
export const browserFileToMd5 = (file) => {
return new Promise((resove, reject) => {
const bmf = new FileMd5()
bmf.md5(file, (err, md5) => {
if (err) reject(err)
resove(md5)
})
})
}
/** 浏览器上传文件 */
export const browserUploadFile = (url, file, addData = {}) => {
return new Promise((resove, reject) => {
uni.uploadFile({
url: BASE_URL + url,
file,
name: 'file',
formData: addData,
timeout: 1000 * 60 * 100,
success({
data
}) {
data = JSON.parse(data)
if (data.code !== 200) reject(data)
resove(data)
},
fail(err) {
reject(err)
}
})
})
}
/* 网络请求 */
export const request = (options = {
type: 'GET',
url: '',
data: {}
}) => {
return new Promise((resove, reject) => {
uni.request({
url: BASE_URL + options.url,
method: options.type,
data: options.data,
success({
data
}) {
if (data.code !== 200) reject(data)
resove(data)
},
fail(err) {
reject(err)
}
})
})
}
//
/* 获取app文件信息 */
export const plus_io_getFileInfo = (filePath) => {
return new Promise((resove, reject) => {
plus.io.getFileInfo({
filePath,
digestAlgorithm: 'md5',
success: res => {
resove(res)
},
fail: (err) => {
reject(err)
}
})
})
}
/* app 文件上传 */
export const plus_uploader = (url, filePath, addData = {}) => {
return new Promise((resove, reject) => {
const task = plus.uploader.createUpload(BASE_URL + url, {
method: "POST",
priority: 0
},
function(res, status) {
if (status !== 200) reject(res)
res = JSON.parse(res.responseText)
if (res.code !== 200) reject(res)
resove(res)
}
);
task.addFile(filePath, {
key: "file"
});
for (let key in addData) {
task.addData(key, addData[key]);
}
task.start();
})
}
export const aesEncrypt = (value) => {
const e = new JSEncrypt()
e.setPublicKey(PUBLIC_KEY);
const s = e.encrypt(value)
return s
}
// rgb 转 16
export function colorRGBtoHex(color) {
var rgb = color.split(',');
var r = parseInt(rgb[0].split('(')[1]);
var g = parseInt(rgb[1]);
var b = parseInt(rgb[2].split(')')[0]);
var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
return hex;
}

View File

@@ -0,0 +1,84 @@
{
"id": "mumu-h5office",
"displayName": "h5office。预览office文件预览文档打开PDF WORD PPT EXCEL 文件",
"version": "1.0.1",
"description": "h5office是兼容 `浏览器端(h5)` `iOS app端` `Android app端` 的文档预览工具。支持权限配置 `下载` `拷贝文字` `打印`",
"keywords": [
"excel",
"PDF",
"PDF预览",
"文档预览"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "u"
},
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}

View File

@@ -0,0 +1,94 @@
# mumu-h5office
## 说明
h5office是兼容 `浏览器端(h5)` `iOS app端` `Android app端` 的文档预览工具。支持权限配置 `下载` `拷贝文字` `打印`。支持app端本地文件预览网络文件预览。手机端支持触屏操作双指放大缩小pc端UI美观好看
### 必读
服务器资源有限每个appid都会有次数限制
使用次数可以在小程序中进行刷新,刷新次数是没有限制的(需观看广告)
小程序中可以设置次数不足邮件提醒,当次数低于设置的数量时会通过邮件进行提示
当前插件的后端服务是共用一台 2核心、4G内存、30M带宽 服务器
可能在高峰期的时候文件预览速度会变慢。
如果对预览文件速度、预览次数有大量的需求,或者大文件预览。请联系开发者部署私有化的版本。
#### 本地文件预览功能说明
本地文件预览的本质,其实是先把文件上传到服务器,再通过服务器进行解析。
如果对文件隐私有需求,只有选择服务器端的服务进行私有部署。
[私有部署说明](https://h5office.cn/price)
### Appid 申请方法
![img](https://h5office.cn/images/getAppid.jpg)
## 简单使用
**插件已支持 uni_modules 支持组件easycom以下代码演示的是普通使用**
```html
<!-- HTML -->
<mumu-h5office v-model="isShow" :file="inputFile" appid="你申请的Appid"></mumu-h5office>
<button @click="openFile">打开网络文件</button>
```
```javascript
// script
import mumuH5office
from "@/uni_modules/mumu-h5office/components/mumu-h5office/mumu-h5office.vue"
export default {
components: { mumuH5office },
data() {
inputFile: '',
isShow: false,
},
methods: {
openFile() {
this.inputFile = "https://h5plugin.mumudev.top/public/previewOffce/333.docx"
this.isShow = true
}
}
}
```
## [官方文档https://h5office.cn](https://h5office.cn)
## 预览
[浏览器中直接打开https://h5plugin.mumudev.top/#/pages/h5office/h5office](https://h5plugin.mumudev.top/#/pages/h5office/h5office)
扫码预览:
![h5office预览](https://h5plugin.mumudev.top/public/h5office/qrcode.png)
下载完整演示代码、安卓app
[官网下载页面https://h5office.cn/uni-app/%E5%AE%89%E8%A3%85](https://h5office.cn/uni-app/%E5%AE%89%E8%A3%85)
## 属性、事件、其他组件
请前往官网进行查看:
[属性和事件文档说明https://h5office.cn/uni-app/%E5%B1%9E%E6%80%A7%E5%92%8C%E4%BA%8B%E4%BB%B6](https://h5office.cn/uni-app/%E5%B1%9E%E6%80%A7%E5%92%8C%E4%BA%8B%E4%BB%B6)
## 支持作者
![支持作者](https://student.mumudev.top/wxMP.jpg)

View File

@@ -70,7 +70,9 @@ function checkStorageSpace() {
function checkUpdate(forceCheck = false) {
const localVersion = plus.runtime.version;
const localWgtVersion = uni.getStorageSync('wgtVersion') || localVersion;
console.log(plus.runtime.version, localWgtVersion)
uni.request({
url: 'http://49.232.154.205:10900/fadapp-update/version.json?t=' + Date.now(),
success: (res) => {

View File

@@ -1,3 +1,8 @@
版本记录规则:
第一个数字表示基座版本,如果第一个数字不同代表基座变更,必须重新安装
第二个数字表示版本大更新,用于一次更新包含了大量的更改
第三个数字表示小更新,用于一些较小的调整
## 4.5.1
+ 开始记录版本
+ 增加版本不兼容时可以从浏览器下载安装包的功能
@@ -53,3 +58,8 @@
## 5.0.0
+ 修改页面启动图
## 5.1.0
+ 增加项目明细页面
+ 增加客户管理页面
+ 下个版本需要增加更多页面