Merge branch 'master' of github.com:kuaifan/dootask into develop

# Conflicts:
#	app/Http/Controllers/Api/DialogController.php
#	electron/package.json
#	package.json
#	public/css/app.css
#	public/js/app.js
#	public/js/build/146.js
#	public/js/build/146.js.LICENSE.txt
#	public/js/build/178.js
#	public/js/build/178.js.LICENSE.txt
#	public/js/build/199.js
#	public/js/build/199.js.LICENSE.txt
#	public/js/build/309.js
#	public/js/build/328.js.LICENSE.txt
#	public/js/build/388.js
#	public/js/build/43.js
#	public/js/build/46.js.LICENSE.txt
#	public/js/build/857.js
#	public/js/build/857.js.LICENSE.txt
#	public/js/build/893.js
This commit is contained in:
kuaifan
2022-01-25 16:11:23 +08:00
1042 changed files with 7455 additions and 81206 deletions

View File

@@ -0,0 +1,379 @@
<script>
import {mapState} from "vuex";
export default {
name: 'AceEditor',
props: {
value: {
default: ''
},
options: {
type: Object,
default: () => ({})
},
theme: {
type: String,
default: 'auto'
},
ext: {
type: String,
default: 'txt'
},
height: {
type: Number || null,
default: null
},
width: {
type: Number || null,
default: null
},
wrap: {
type: Boolean,
default: false
},
readOnly: {
type: Boolean,
default: false
},
},
render(createElement) {
return createElement('div', {
class: "no-dark-mode"
})
},
data: () => ({
code: '',
editor: null,
cursorPosition: {
row: 0,
column: 0
},
supportedModes: {
"Apache_Conf": [
"^htaccess|^htgroups|^htpasswd|^conf|htaccess|htgroups|htpasswd"
],
"BatchFile": [
"bat|cmd"
],
"C_Cpp": [
"cpp|c|cc|cxx|h|hh|hpp|ino"
],
"CSharp": [
"cs"
],
"CSS": [
"css"
],
"Dockerfile": [
"^Dockerfile"
],
"golang": [
"go"
],
"HTML": [
"html|htm|xhtml|vue|we|wpy"
],
"Java": [
"java"
],
"JavaScript": [
"js|jsm|jsx"
],
"JSON": [
"json"
],
"JSP": [
"jsp"
],
"LESS": [
"less"
],
"Lua": [
"lua"
],
"Makefile": [
"^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"
],
"Markdown": [
"md|markdown"
],
"MySQL": [
"mysql"
],
"Nginx": [
"nginx|conf"
],
"INI": [
"ini|conf|cfg|prefs"
],
"ObjectiveC": [
"m|mm"
],
"Perl": [
"pl|pm"
],
"Perl6": [
"p6|pl6|pm6"
],
"pgSQL": [
"pgsql"
],
"PHP_Laravel_blade": [
"blade.php"
],
"PHP": [
"php|inc|phtml|shtml|php3|php4|php5|phps|phpt|aw|ctp|module"
],
"Powershell": [
"ps1"
],
"Python": [
"py"
],
"R": [
"r"
],
"Ruby": [
"rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"
],
"Rust": [
"rs"
],
"SASS": [
"sass"
],
"SCSS": [
"scss"
],
"SH": [
"sh|bash|^.bashrc"
],
"SQL": [
"sql"
],
"SQLServer": [
"sqlserver"
],
"Swift": [
"swift"
],
"Text": [
"txt"
],
"Typescript": [
"ts|typescript|str"
],
"VBScript": [
"vbs|vb"
],
"Verilog": [
"v|vh|sv|svh"
],
"XML": [
"xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl|xaml"
],
"YAML": [
"yaml|yml"
],
"Compress": [
"tar|zip|7z|rar|gz|arj|z"
],
"images": [
"icon|jpg|jpeg|png|bmp|gif|tif|emf"
]
},
}),
mounted() {
$A.loadScriptS([
'js/ace/ace.js',
'js/ace/mode-json.js',
], () => {
// set init editor size
this.setSize(this.$el, {height: this.height, width: this.width})
// init ace editor
this.editor = window.ace.edit(this.$el, {
wrap: this.wrap,
showPrintMargin: false,
readOnly: this.readOnly,
keyboardHandler: 'vscode',
})
this.editor.session.setMode(`ace/mode/${this.getFileMode()}`)
// emit 'mounted' event
this.$emit('mounted', this.editor)
// official syntax validation workers include 'coffee', 'css', 'html'
// 'javascript', 'json', 'lua', 'php', 'xml' and 'xquery'
if (this.editor.session.$worker) {
this.editor.session.$worker.addEventListener('annotate', this.workerMessage, false)
}
// set value and clear selection
this.editor.setValue(this.value)
this.editor.clearSelection()
// set ace editor options and theme
this.editor.setOptions(this.options)
this.editTheme && this.editor.setTheme(`ace/theme/${this.editTheme}`)
// 设置快捷键
this.editor.commands.addCommand({
name: '保存文件',
bindKey: {
win: 'Ctrl-S',
mac: 'Command-S'
},
exec: () => {
this.$emit("saveData")
},
readOnly: false
});
// 触发修改内容
this.editor.getSession().on('change', () => {
this.code = this.editor.getValue()
this.$emit('input', this.code);
});
});
},
methods: {
/**
* listening lint events from worker
* @param data
*/
workerMessage({data}) {
// record current cursor position
this.cursorPosition = this.editor.selection.getCursor()
const [validationInfo] = data
if (validationInfo && validationInfo.type === 'error') {
this.$emit('validationFailed', validationInfo)
} else {
this.$emit('change', this.editor.getValue())
}
},
/**
* set editor size
* @param dom
* @param width
* @param height
*/
setSize(dom, {width = this.width, height = this.height}) {
dom.style.width = width && typeof width === 'number' ? `${width}px` : '100%'
dom.style.height = height && typeof height === 'number' ? `${height}px` : '100%'
this.$nextTick(() => this.editor && this.editor.resize())
},
/**
* 获取文件类型
* @returns {string}
*/
getFileMode() {
var ext = this.ext || "text";
for (var name in this.supportedModes) {
var data = this.supportedModes[name],
suffixs = data[0].split('|'),
mode = name.toLowerCase();
for (var i = 0; i < suffixs.length; i++) {
if (ext == suffixs[i]) {
return mode;
}
}
}
return 'text';
}
},
computed: {
...mapState(['themeIsDark']),
editTheme() {
if (this.theme == 'auto') {
if (this.themeIsDark) {
return "dracula-dark"
} else {
return "chrome"
}
}
return this.theme
}
},
watch: {
/**
* watching and set options
* @param newOptions ace editor options
*/
options(newOptions) {
if (newOptions && typeof newOptions === 'object') {
this.editor && this.editor.setOptions(newOptions)
}
},
/**
* watching and set theme
* @param newTheme
*/
editTheme(newTheme) {
if (newTheme && typeof newTheme === 'string') {
this.editor && this.editor.setTheme(`ace/theme/${newTheme}`)
}
},
/**
* watching and set ext
* @param newExt
*/
ext(newExt) {
if (newExt && typeof newExt === 'string') {
this.editor && this.editor.session.setMode(`ace/mode/${this.getFileMode()}`)
}
},
/**
* watching and set width
* @param newWidth
*/
width(newWidth) {
this.setSize(this.el, {width: newWidth})
},
/**
* watching and set height
* @param newHeight
*/
height(newHeight) {
this.setSize(this.el, {height: newHeight})
},
/**
* watching and set readOnly
* @param only
*/
readOnly(only) {
if (typeof only === 'boolean') {
this.editor && this.editor.setReadOnly(only)
}
},
/**
* watching and set code
* @param newCode
*/
value(newCode) {
if (!this.editor) {
return
}
if (newCode == this.code) {
return;
}
this.editor.setValue(newCode)
this.editor.clearSelection()
const {row, column} = this.cursorPosition
// move cursor to current position
this.editor.selection.moveCursorTo(row, column)
}
},
beforeDestroy() {
if (this.editor) {
if (this.editor.session.$worker) {
this.editor.session.$worker.removeEventListener('message', this.workerMessage, false)
}
this.editor.destroy()
this.editor.container.remove()
}
}
}
</script>

View File

@@ -1,318 +0,0 @@
<template>
<div :id="id" class="luckysheet-component">
<Loading v-if="loadIng" class="luckysheet-loading"/>
</div>
</template>
<style lang="scss" scoped>
.luckysheet-component {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
.luckysheet-loading {
width: 32px;
height: 32px;
}
}
</style>
<script>
import XLSX from "xlsx";
export default {
name: 'LuckySheet',
props: {
id: {
type: String,
default: () => {
return "luckysheet_" + Math.round(Math.random() * 10000);
}
},
value: {
type: [Object, Array],
default: function () {
return {}
}
},
readOnly: {
type: Boolean,
default: false
},
},
data() {
return {
loadIng: true,
sheetLoaded: false,
bakValue: '',
}
},
mounted() {
$A.loadScriptS([
'js/luckysheet/plugins/css/pluginsCss.css',
'js/luckysheet/plugins/plugins.css',
'js/luckysheet/css/luckysheet.css',
'js/luckysheet/assets/iconfont/iconfont.css',
//
'js/luckysheet/plugins/js/plugin.js',
'js/luckysheet/luckysheet.umd.js',
], () => {
this.loadIng = false;
this.bakValue = JSON.stringify(this.value);
this.loadSheet(this.value);
});
},
watch: {
value: {
handler(value) {
if (this.bakValue == JSON.stringify(value)) {
return;
}
this.bakValue = JSON.stringify(value);
this.loadSheet(value);
},
deep: true
}
},
methods: {
loadSheet(value) {
let lang = 'zh';
switch (this.getLanguage()) {
case 'CN':
case 'TC':
lang = 'zh';
break;
default:
lang = 'en';
break;
}
//
this.sheetLoaded && window.luckysheet.destroy();
this.sheetLoaded = true;
let config = {
container: this.id,
showinfobar: false,
plugins: [
'chart'
],
showtoolbarConfig: {
chart: false
},
cellRightClickConfig: {
chart: false
},
lang: lang,
loading: {
image: 'image://' + $A.originUrl('js/luckysheet/css/loading.gif')
},
data: $A.isArray(value) ? $A.cloneJSON(value) : [
{
"name": "Sheet1",
}
],
hook:{
updated: () => {
this.updateData();
},
sheetActivate: () => {
this.$nextTick(this.updateData);
}
},
};
if (this.readOnly) {
config.showtoolbar = false;
config.allowEdit = false;
config.enableAddRow = false;
config.enableAddBackTop = false;
config.showsheetbarConfig = {
add: false
};
config.sheetRightClickConfig = {
delete: false, // 删除
copy: false, // 复制
rename: false, //重命名
color: false, //更改颜色
hide: false, //隐藏,取消隐藏
move: false, //向左移,向右移
};
}
window.luckysheet.create(config);
},
updateData() {
const value = window.luckysheet.getAllSheets();
value.forEach((item) => {
delete item.ch_width;
delete item.rh_height;
delete item.index;
});
this.bakValue = JSON.stringify(value);
this.$emit('input', value);
},
chatatABC(n) {
var orda = 'a'.charCodeAt(0);
var ordz = 'z'.charCodeAt(0);
var len = ordz - orda + 1;
var s = "";
while (n >= 0) {
s = String.fromCharCode(n % len + orda) + s;
n = Math.floor(n / len) - 1;
}
return s.toUpperCase();
},
exportExcel(bookName, bookType) {
let allSheetData = window.luckysheet.getluckysheetfile();
let SheetNames = [];
let Sheets = {};
allSheetData.forEach((sheetData) => {
let downOriginData = sheetData.data;
let arr = []; // 所有的单元格数据组成的二维数组
let bgConfig = {};
let cellValue = null;
// 获取单元格的背景色
let setBackground = (row, col, bg) => {
var colA = this.chatatABC(col);
var key = colA + (row + 1);
bgConfig[key] = bg.replace(/\#?/, '');
}
// 获取二维数组
for (let row = 0; row < downOriginData.length; row++) {
let arrRow = [];
for (let col = 0; col < downOriginData[row].length; col++) {
cellValue = downOriginData[row][col]
if (cellValue) {
// 处理单元格的背景颜色
if (cellValue.bg) {
setBackground(row, col, cellValue.bg)
}
if (cellValue.ct != null && cellValue.ct.t == 'd') {
// d为时间格式 2019-01-01 或者2019-01-01 10:10:10
arrRow.push(new Date(cellValue.m.replace(/\-/g, '/'))) //兼容IE
} else if (cellValue.m && this.isPercentage(cellValue)) {
//百分比问题
arrRow.push(cellValue.m)
} else {
arrRow.push(cellValue.v)
}
}
}
arr.push(arrRow)
}
let opts = {
dateNF: 'm/d/yy h:mm',
cellDates: true,
cellStyles: true
}
let ws = XLSX.utils.aoa_to_sheet(arr, opts)
//
let reg = /[\u4e00-\u9fa5]/g;
for (let key in ws) {
if (!ws.hasOwnProperty(key)) {
continue;
}
let item = ws[key]
if (item.t === 'd') {
if (item.w) {
//时间格式的设置
let arr = item.w.split(' ')
if (arr[1] && arr[1] == '0:00') {
ws[key].z = 'm/d/yy'
} else {
item.z = 'yyyy/m/d h:mm:ss'
}
}
} else if (item.t === 's') {
//百分比设置格式
if (item.v && !item.v.match(reg) && item.v.indexOf('%') > -1) {
item.t = 'n'
item.z = '0.00%'
item.v = Number.parseFloat(item.v) / 100
} else if (item.v && item.v.match(reg)) {
//含有中文的设置居中样式
item['s'] = {
alignment: {vertical: 'center', horizontal: 'center'}
}
}
}
// 设置单元格样式
if (bgConfig[key]) {
ws[key]['s'] = {
alignment: {vertical: 'center', horizontal: 'center'},
fill: {bgColor: {indexed: 32}, fgColor: {rgb: bgConfig[key]}},
border: {
top: {style: 'thin', color: {rgb: '999999'}},
bottom: {style: 'thin', color: {rgb: '999999'}},
left: {style: 'thin', color: {rgb: '999999'}},
right: {style: 'thin', color: {rgb: '999999'}}
}
}
}
}
// 内容
SheetNames.push(sheetData.name)
Sheets[sheetData.name] = Object.assign({}, ws);
// 合并单元格配置
let mergeConfig = sheetData.config.merge
let mergeArr = [];
if (JSON.stringify(mergeConfig) !== '{}') {
mergeArr = this.handleMergeData(mergeConfig)
Sheets[sheetData.name]['!merges'] = mergeArr
}
});
//
const data = {
SheetNames: SheetNames,
Sheets: Sheets
}
const opts = {
bookType: bookType || "xlsx"
}
const filename = bookName + "." + (bookType == 'xlml' ? 'xls' : bookType);
if (this.$Electron) {
this.$Electron.ipcRenderer.send('saveSheet', data, filename, opts);
} else {
XLSX.writeFile(data, filename, opts);
}
},
isPercentage(value) {
return /%$/.test(value.m) && value.ct && value.ct.t === 'n'
},
handleMergeData(origin) {
let result = []
if (origin instanceof Object) {
var r = "r",
c = "c",
cs = "cs",
rs = "rs";
for (var key in origin) {
if (!origin.hasOwnProperty(key)) {
continue;
}
var startR = origin[key][r];
var endR = origin[key][r];
var startC = origin[key][c];
var endC = origin[key][c];
// 如果只占一行 为1 如果占两行 为2
if (origin[key][cs] > 0) {
endC = startC + (origin[key][cs] - 1);
}
if (origin[key][rs] > 0) {
endR = startR + (origin[key][rs] - 1);
}
// s为合并单元格的开始坐标 e为结束坐标
var obj = {s: {"r": startR, "c": startC}, e: {"r": endR, "c": endC}}
result.push(obj)
}
}
return result
}
}
}
</script>

View File

@@ -355,6 +355,23 @@
*/
dialogCompleted(dialog) {
return this.dialogTags(dialog).find(({color}) => color == 'success');
},
/**
* 下载文件
* @param url
*/
downFile(url) {
if (!url) {
return
}
if ($A.Electron) {
$A.Electron.shell.openExternal(url).catch(() => {
$A.modalError("下载失败");
});
} else {
window.open(url)
}
}
});

View File

@@ -9,7 +9,7 @@
<div v-else-if="msgData.type === 'loading'" class="dialog-content loading"><Loading/></div>
<!--文件-->
<div v-else-if="msgData.type === 'file'" :class="['dialog-content', msgData.msg.type]">
<a :href="msgData.msg.path" target="_blank">
<div class="dialog-file" @click="downFile">
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb"/>
<div v-else class="file-box">
<img class="file-thumb" :src="msgData.msg.thumb"/>
@@ -18,7 +18,7 @@
<div class="file-size">{{$A.bytesToSize(msgData.msg.size)}}</div>
</div>
</div>
</a>
</div>
</div>
<!--未知-->
<div v-else class="dialog-content unknown">{{$L("未知的消息类型")}}</div>
@@ -56,6 +56,7 @@
<script>
import WCircle from "../../../components/WCircle";
import {mapState} from "vuex";
export default {
name: "DialogView",
@@ -84,6 +85,8 @@ export default {
},
computed: {
...mapState(['userToken']),
readList() {
return this.read_list.filter(({read_at}) => read_at)
},
@@ -162,6 +165,17 @@ export default {
};
}
return {};
},
downFile() {
$A.modalConfirm({
title: '下载文件',
content: `${this.msgData.msg.name} (${$A.bytesToSize(this.msgData.msg.size)})`,
okText: '立即下载',
onOk: () => {
$A.downFile($A.apiUrl(`dialog/msg/download?msg_id=${this.msgData.id}&token=${this.userToken}`))
}
});
}
}
}

View File

@@ -341,8 +341,21 @@ export default {
const postFiles = Array.prototype.slice.call(files);
if (postFiles.length > 0) {
e.preventDefault();
postFiles.forEach((file) => {
this.$refs.chatUpload.upload(file);
this.pasteFile = [];
this.pasteItem = [];
postFiles.some(file => {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = ({target}) => {
this.pasteFile.push(file)
this.pasteItem.push({
type: $A.getMiddle(file.type, null, '/'),
name: file.name,
size: file.size,
result: target.result
})
this.pasteShow = true
}
});
}
},

View File

@@ -33,18 +33,12 @@
<div v-if="file.type=='mind'" class="header-hint">
{{$L('选中节点按enter键添加同级节点tab键添加子节点')}}
</div>
<Dropdown v-if="file.type=='mind' || file.type=='flow' || file.type=='sheet'"
<Dropdown v-if="file.type=='mind' || file.type=='flow'"
trigger="click"
class="header-hint"
@on-click="exportMenu">
<a href="javascript:void(0)">{{$L('导出')}}<Icon type="ios-arrow-down"></Icon></a>
<DropdownMenu v-if="file.type=='sheet'" slot="list">
<DropdownItem name="xlsx">{{$L('导出XLSX')}}</DropdownItem>
<DropdownItem name="xlml">{{$L('导出XLS')}}</DropdownItem>
<DropdownItem name="csv">{{$L('导出CSV')}}</DropdownItem>
<DropdownItem name="txt">{{$L('导出TXT')}}</DropdownItem>
</DropdownMenu>
<DropdownMenu v-else slot="list">
<DropdownMenu slot="list">
<DropdownItem name="png">{{$L('导出PNG图片')}}</DropdownItem>
<DropdownItem name="pdf">{{$L('导出PDF文件')}}</DropdownItem>
</DropdownMenu>
@@ -58,8 +52,8 @@
</template>
<Flow v-else-if="file.type=='flow'" ref="myFlow" v-model="contentDetail" @saveData="handleClick('saveBefore')"/>
<Minder v-else-if="file.type=='mind'" ref="myMind" v-model="contentDetail" @saveData="handleClick('saveBefore')"/>
<LuckySheet v-else-if="file.type=='sheet'" ref="mySheet" v-model="contentDetail"/>
<OnlyOffice v-else-if="['word', 'excel', 'ppt'].includes(file.type)" v-model="contentDetail"/>
<AceEditor v-else-if="['code', 'txt'].includes(file.type)" v-model="contentDetail.content" :ext="file.ext" @saveData="handleClick('saveBefore')"/>
</div>
</template>
<div v-if="contentLoad" class="content-load"><Loading/></div>
@@ -74,13 +68,13 @@ Vue.use(Minder)
const MDEditor = () => import('../../../components/MDEditor/index');
const TEditor = () => import('../../../components/TEditor');
const LuckySheet = () => import('../../../components/LuckySheet');
const Flow = () => import('../../../components/Flow');
const AceEditor = () => import('../../../components/AceEditor');
const OnlyOffice = () => import('../../../components/OnlyOffice');
export default {
name: "FileContent",
components: {TEditor, MDEditor, LuckySheet, Flow, OnlyOffice},
components: {AceEditor, TEditor, MDEditor, Flow, OnlyOffice},
props: {
value: {
type: Boolean,
@@ -227,10 +221,12 @@ export default {
getContent() {
if (!this.fileId) {
this.contentDetail = {};
this.updateBak();
return;
}
if (typeof this.fileContent[this.fileId] !== "undefined") {
this.contentDetail = this.fileContent[this.fileId];
this.updateBak();
return;
}
if (['word', 'excel', 'ppt'].includes(this.file.type)) {
@@ -309,10 +305,6 @@ export default {
case 'flow':
this.$refs.myFlow[act == 'pdf' ? 'exportPDF' : 'exportPNG'](this.file.name, 3);
break;
case 'sheet':
this.$refs.mySheet.exportExcel(this.file.name, act);
break;
}
},

View File

@@ -11,18 +11,12 @@
<Icon v-else type="ios-refresh" @click="getContent" />
</div>
</div>
<Dropdown v-if="file.type=='mind' || file.type=='flow' || file.type=='sheet'"
<Dropdown v-if="file.type=='mind' || file.type=='flow'"
trigger="click"
class="header-hint"
@on-click="exportMenu">
<a href="javascript:void(0)">{{$L('导出')}}<Icon type="ios-arrow-down"></Icon></a>
<DropdownMenu v-if="file.type=='sheet'" slot="list">
<DropdownItem name="xlsx">{{$L('导出XLSX')}}</DropdownItem>
<DropdownItem name="xlml">{{$L('导出XLS')}}</DropdownItem>
<DropdownItem name="csv">{{$L('导出CSV')}}</DropdownItem>
<DropdownItem name="txt">{{$L('导出TXT')}}</DropdownItem>
</DropdownMenu>
<DropdownMenu v-else slot="list">
<DropdownMenu slot="list">
<DropdownItem name="png">{{$L('导出PNG图片')}}</DropdownItem>
<DropdownItem name="pdf">{{$L('导出PDF文件')}}</DropdownItem>
</DropdownMenu>
@@ -35,8 +29,8 @@
</template>
<Flow v-else-if="file.type=='flow'" ref="myFlow" v-model="contentDetail" readOnly/>
<Minder v-else-if="file.type=='mind'" ref="myMind" v-model="contentDetail" readOnly/>
<LuckySheet v-else-if="file.type=='sheet'" ref="mySheet" v-model="contentDetail" readOnly/>
<OnlyOffice v-else-if="['word', 'excel', 'ppt'].includes(file.type)" v-model="contentDetail" :code="code" readOnly/>
<AceEditor v-else-if="['code', 'txt'].includes(file.type)" v-model="contentDetail.content" :ext="file.ext" readOnly/>
</div>
</template>
<div v-if="contentLoad" class="content-load"><Loading/></div>
@@ -50,13 +44,13 @@ Vue.use(Minder)
const MDPreview = () => import('../../../components/MDEditor/preview');
const TEditor = () => import('../../../components/TEditor');
const LuckySheet = () => import('../../../components/LuckySheet');
const Flow = () => import('../../../components/Flow');
const AceEditor = () => import('../../../components/AceEditor');
const OnlyOffice = () => import('../../../components/OnlyOffice');
export default {
name: "FilePreview",
components: {TEditor, MDPreview, LuckySheet, Flow, OnlyOffice},
components: {AceEditor, TEditor, MDPreview, Flow, OnlyOffice},
props: {
code: {
type: String,
@@ -159,10 +153,6 @@ export default {
case 'flow':
this.$refs.myFlow[act == 'pdf' ? 'exportPDF' : 'exportPNG'](this.file.name, 3);
break;
case 'sheet':
this.$refs.mySheet.exportExcel(this.file.name, act);
break;
}
},

View File

@@ -35,7 +35,7 @@
</ETooltip>
</li>
<li :class="['project-icon', searchText!='' ? 'active' : '']">
<Tooltip :always="searchText!=''" @on-popper-show="searchFocus" theme="light">
<Tooltip :always="searchAlways" @on-popper-show="searchFocus" theme="light">
<Icon class="menu-icon" type="ios-search" @click="searchFocus" />
<div slot="content">
<Input v-model="searchText" ref="searchInput" :placeholder="$L('名称、描述...')" class="search-input" clearable/>
@@ -550,6 +550,17 @@ export default {
...mapGetters(['projectData', 'projectParameter', 'transforTasks']),
searchAlways() {
return !(!this.searchText
|| this.settingShow
|| this.userShow
|| this.inviteShow
|| this.transferShow
|| this.workflowShow
|| this.logShow
|| this.archivedTaskShow);
},
userWaitRemove() {
const {userids, useridbak} = this.userData;
if (!userids) {

View File

@@ -216,11 +216,14 @@ export default {
return list
}
if (this.taskId > 0 && $A.isJson(record.flow)) {
list.push({
id,
button: '重置',
content: `确定重置为【${$A.getMiddle(record.flow.flow_item_name, "|")}】吗?`,
})
let name = $A.getMiddle(record.flow.flow_item_name, "|")
if (name) {
list.push({
id,
button: '重置',
content: `确定重置为【${name}】吗?`,
})
}
}
return list;
},

View File

@@ -274,7 +274,7 @@
<li v-for="file in fileList">
<img v-if="file.id" class="file-ext" :src="file.thumb"/>
<Loading v-else class="file-load"/>
<a class="file-name" :href="file.path||'javascript:;'" target="_blank">{{file.name}}</a>
<div class="file-name" @click="downFile(file)">{{file.name}}</div>
<div class="file-size">{{$A.bytesToSize(file.size)}}</div>
<EPopover v-model="file._deling" class="file-delete">
<div class="task-detail-delete-file-popover">
@@ -517,6 +517,7 @@ export default {
computed: {
...mapState([
'userId',
'userToken',
'cacheProjects',
'cacheColumns',
'cacheTasks',
@@ -916,7 +917,7 @@ export default {
onAddsub() {
if (this.addsubName == '') {
$A.messageSuccess('任务描述不能为空');
$A.messageError('任务描述不能为空');
return;
}
this.addsubLoad++;
@@ -1131,6 +1132,17 @@ export default {
}
}, 100);
}
},
downFile(file) {
$A.modalConfirm({
title: '下载文件',
content: `${file.name} (${$A.bytesToSize(file.size)})`,
okText: '立即下载',
onOk: () => {
$A.downFile($A.apiUrl(`project/task/filedown?file_id=${file.id}&token=${this.userToken}`))
}
});
}
}
}

View File

@@ -104,8 +104,14 @@
</template>
<div class="file-menu" :style="contextMenuStyles">
<Dropdown trigger="custom" :visible="contextMenuVisible" transfer @on-clickoutside="handleClickContextMenuOutside" @on-visible-change="handleVisibleChangeMenu">
<DropdownMenu slot="list" class="page-file-dropdown-menu">
<Dropdown
trigger="custom"
:visible="contextMenuVisible"
transfer-class-name="page-file-dropdown-menu"
@on-clickoutside="handleClickContextMenuOutside"
@on-visible-change="handleVisibleChangeMenu"
transfer>
<DropdownMenu slot="list">
<template v-if="contextMenuItem.id">
<DropdownItem @click.native="handleContextClick('open')">{{$L('打开')}}</DropdownItem>
<Dropdown placement="right-start" transfer>
@@ -133,6 +139,7 @@
<template v-else-if="contextMenuItem.share">
<DropdownItem @click.native="handleContextClick('outshare')" divided>{{$L('退出共享')}}</DropdownItem>
</template>
<DropdownItem @click.native="handleContextClick('download')" :disabled="contextMenuItem.ext == ''">{{$L('下载')}}</DropdownItem>
<DropdownItem @click.native="handleContextClick('delete')" divided style="color:red">{{$L('删除')}}</DropdownItem>
</template>
<template v-else>
@@ -153,12 +160,12 @@
<div v-if="uploadShow && uploadList.length > 0" class="file-upload-list">
<div class="upload-wrap">
<div class="title">
{{$L('上传列表')}}
{{$L('上传列表')}} ({{uploadList.length}})
<em v-if="uploadList.find(({status}) => status === 'finished')" @click="uploadClear">{{$L('清空已完成')}}</em>
</div>
<ul class="content">
<li v-for="(item, index) in uploadList">
<AutoTip class="file-name">{{item.name}}</AutoTip>
<li v-for="(item, index) in uploadList" :key="index" v-if="index < 100">
<AutoTip class="file-name">{{uploadName(item)}}</AutoTip>
<AutoTip v-if="item.status === 'finished' && item.response && item.response.ret !== 1" class="file-error">{{item.response.msg}}</AutoTip>
<Progress v-else :percent="uploadPercentageParse(item.percentage)" :stroke-width="5" />
<Icon class="file-close" type="ios-close-circle-outline" @click="uploadList.splice(index, 1)"/>
@@ -344,11 +351,6 @@ export default {
"name": "文本",
"divided": true
},
{
"value": "sheet",
"label": null,
"name": "表格",
},
{
"value": "flow",
"label": "流程图",
@@ -406,8 +408,13 @@ export default {
'ofd',
'pdf',
'txt',
'html', 'htm', 'asp', 'jsp', 'xml', 'json', 'properties', 'md', 'gitignore', 'log', 'java', 'py', 'c', 'cpp', 'sql', 'sh', 'bat', 'm', 'bas', 'prg', 'cmd',
'php', 'go', 'python', 'js', 'ftl', 'css', 'lua', 'rb', 'yaml', 'yml', 'h', 'cs', 'aspx',
'htaccess', 'htgroups', 'htpasswd', 'conf', 'bat', 'cmd', 'cpp', 'c', 'cc', 'cxx', 'h', 'hh', 'hpp', 'ino', 'cs', 'css',
'dockerfile', 'go', 'html', 'htm', 'xhtml', 'vue', 'we', 'wpy', 'java', 'js', 'jsm', 'jsx', 'json', 'jsp', 'less', 'lua', 'makefile', 'gnumakefile',
'ocamlmakefile', 'make', 'md', 'markdown', 'mysql', 'nginx', 'ini', 'cfg', 'prefs', 'm', 'mm', 'pl', 'pm', 'p6', 'pl6', 'pm6', 'pgsql', 'php',
'inc', 'phtml', 'shtml', 'php3', 'php4', 'php5', 'phps', 'phpt', 'aw', 'ctp', 'module', 'ps1', 'py', 'r', 'rb', 'ru', 'gemspec', 'rake', 'guardfile', 'rakefile',
'gemfile', 'rs', 'sass', 'scss', 'sh', 'bash', 'bashrc', 'sql', 'sqlserver', 'swift', 'ts', 'typescript', 'str', 'vbs', 'vb', 'v', 'vh', 'sv', 'svh', 'xml',
'rdf', 'rss', 'wsdl', 'xslt', 'atom', 'mathml', 'mml', 'xul', 'xbl', 'xaml', 'yaml', 'yml',
'asp', 'properties', 'gitignore', 'log', 'bas', 'prg', 'python', 'ftl', 'aspx',
'mp3', 'wav', 'mp4', 'flv',
'avi', 'mov', 'wmv', 'mkv', '3gp', 'rm',
'xmind', 'rp',
@@ -903,6 +910,21 @@ export default {
this.linkGet()
break;
case 'download':
if (!item.ext) {
$A.modalError("此文件不支持下载");
return;
}
$A.modalConfirm({
title: '下载文件',
content: `${item.name}.${item.ext} (${$A.bytesToSize(item.size)})`,
okText: '立即下载',
onOk: () => {
$A.downFile($A.apiUrl(`file/content?id=${item.id}&down=yes&token=${this.userToken}`))
}
});
break;
case 'delete':
let typeName = item.type == 'folder' ? '文件夹' : '文件';
$A.modalConfirm({
@@ -1142,6 +1164,10 @@ export default {
})
},
uploadName(item) {
return $A.getObject(item, 'response.data.full_name') || item.name
},
/********************文件上传部分************************/
uploadUpdate(fileList) {