matrix_components 2.0.300 → 2.0.302
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/matrix_components.css +1 -1
- package/dist/matrix_components.js +15011 -2107
- package/dist/matrix_components.umd.cjs +1 -1
- package/package.json +6 -4
- package/dist/ComponentDemo/DialogDemo.vue +0 -208
- package/dist/ComponentDemo/ExcelDemo.vue +0 -263
- package/dist/ComponentDemo/OfficeDemo.vue +0 -189
- package/dist/ComponentDemo/PdfDemo.vue +0 -207
- package/dist/ComponentDemo/Test.vue +0 -6
- package/dist/ComponentDemo/VideoDemo.vue +0 -273
- package/dist/ComponentDemo/WordDemo.vue +0 -191
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matrix_components",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.302",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"dev": "vite",
|
|
25
|
-
"build": "
|
|
25
|
+
"build": "vite build",
|
|
26
26
|
"preview": "vite preview",
|
|
27
27
|
"build-only": "vite build",
|
|
28
28
|
"type-check": "vue-tsc --build",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"docx-preview": "^0.3.6",
|
|
39
|
+
"konva": "^10.0.2",
|
|
40
|
+
"vue-konva": "^3.2.6",
|
|
39
41
|
"vue3-pdf-app": "^1.0.3",
|
|
40
42
|
"x-data-spreadsheet": "^1.1.9",
|
|
41
43
|
"xlsx": "^0.18.5"
|
|
@@ -66,6 +68,7 @@
|
|
|
66
68
|
"peerDependencies": {
|
|
67
69
|
"@element-plus/icons-vue": "^2.3.1",
|
|
68
70
|
"@types/vue": "^2.0.0",
|
|
71
|
+
"@vue/test-utils": "^2.4.6",
|
|
69
72
|
"axios": "^1.9.0",
|
|
70
73
|
"element-plus": "^2.10.5",
|
|
71
74
|
"jsdom": "^27.0.0",
|
|
@@ -75,7 +78,6 @@
|
|
|
75
78
|
"sm-crypto": "^0.3.13",
|
|
76
79
|
"vitest": "^3.2.4",
|
|
77
80
|
"vue": "^3.5.13",
|
|
78
|
-
"vue-router": "^4.5.0"
|
|
79
|
-
"@vue/test-utils": "^2.4.6"
|
|
81
|
+
"vue-router": "^4.5.0"
|
|
80
82
|
}
|
|
81
83
|
}
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<el-button type="warning" @click="openDialog({ params: { test: '222' } })">弹出框组件</el-button>
|
|
4
|
-
<el-button type="primary" @click="updateDialogOption">更新弹窗选项</el-button>
|
|
5
|
-
<el-button type="success" @click="callDialogMethod">调用弹窗方法</el-button>
|
|
6
|
-
<!-- 为每个打开的弹窗生成一个关闭按钮 -->
|
|
7
|
-
<div v-for="(instance, index) in dialogInstances" :key="instance.id" style="margin-top: 10px;">
|
|
8
|
-
<el-button type="danger" @click="closeDialog(instance)">关闭弹窗 {{ index + 1 }}</el-button>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<!-- 全部关闭按钮 -->
|
|
12
|
-
<el-button v-if="dialogInstances.length > 0" type="danger" @click="closeAllDialogs" style="margin-top: 10px;">关闭所有弹窗</el-button>
|
|
13
|
-
</div>
|
|
14
|
-
</template>
|
|
15
|
-
<script setup lang="ts">
|
|
16
|
-
import VideoDemo from '@/views/VideoDemo.vue'
|
|
17
|
-
import { onMounted, onUnmounted, ref } from 'vue'
|
|
18
|
-
|
|
19
|
-
// 扩展Window接口
|
|
20
|
-
declare global {
|
|
21
|
-
interface Window {
|
|
22
|
-
__dialogInstances: any[];
|
|
23
|
-
NsDialog: any;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 保存当前打开的弹窗实例数组
|
|
28
|
-
const dialogInstances = ref<any[]>([])
|
|
29
|
-
const openIndex = ref(0);
|
|
30
|
-
function openDialog(data = {}) {
|
|
31
|
-
openIndex.value++;
|
|
32
|
-
if(dialogInstances.value.length === 0) {
|
|
33
|
-
openIndex.value = 0;
|
|
34
|
-
}
|
|
35
|
-
window.NsDialog(
|
|
36
|
-
{
|
|
37
|
-
class: 'xxx',
|
|
38
|
-
title: '测试',
|
|
39
|
-
// 任何组件添加 $emit('close') 时,会触发关闭弹出框事件
|
|
40
|
-
dom: VideoDemo, // 也可以通过异步方式:import("@/views/VideoDemo.vue") 和 () => import("@/views/VideoDemo.vue")
|
|
41
|
-
domCompleted: (domRef: any)=>{
|
|
42
|
-
// dom加载完成或触发函数,domRef为dom实例可以执行defineExpose暴露出的函数
|
|
43
|
-
console.log('组件加载完成,domRef:', domRef)
|
|
44
|
-
domRef?.xxx?.()
|
|
45
|
-
},
|
|
46
|
-
option: {
|
|
47
|
-
// dom对应的自定义组件props属性
|
|
48
|
-
...data,
|
|
49
|
-
},
|
|
50
|
-
events: {
|
|
51
|
-
// dom组件内部自定义事件emit('btnClick', xxx)
|
|
52
|
-
btnClick: () => {
|
|
53
|
-
console.log('点击中间区域内容')
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
width: '800px', // 宽度, 整个弹出框的高度,非内容高度
|
|
57
|
-
height: '450px', // 高度, 不配置则默认为内容高度
|
|
58
|
-
dialogPadding: [10, 20], // 弹窗内padding
|
|
59
|
-
// 弹窗绝对定位
|
|
60
|
-
x: 250 + openIndex.value * 20,
|
|
61
|
-
y: 100 + openIndex.value * 20,
|
|
62
|
-
// 设置函数时,则有放大和还原按钮,且按返回的对象设置弹出框。(会关闭拖动功能)
|
|
63
|
-
// maxSize: function () {
|
|
64
|
-
// return { width: '100%', height: '800px', x: 0, y: 100 }
|
|
65
|
-
// },
|
|
66
|
-
modal: false, // 模态框
|
|
67
|
-
modalColor: 'rgb(0 21 115 / 20%)', // 遮罩层颜色
|
|
68
|
-
showFooter: true, // 默认显示底部按钮
|
|
69
|
-
immediately: false, // true立即取消弹出框, false异步请求后取消弹出框,默认false
|
|
70
|
-
draggable: true, // 是否可拖拽,默认false
|
|
71
|
-
// 底部确认按钮回调事件
|
|
72
|
-
confirm: async (closeFn: any, componentRef: any, footerLoading: any) => {
|
|
73
|
-
// 2.componentRef可以调用内部函数,前提需要defineExpose
|
|
74
|
-
try {
|
|
75
|
-
const selectRows = componentRef?.value?.getSelectedRows()
|
|
76
|
-
console.log('点击确认,选择数据:', selectRows)
|
|
77
|
-
} catch (e) {
|
|
78
|
-
console.log(e)
|
|
79
|
-
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
80
|
-
}
|
|
81
|
-
// 3.footerLoading可以控制底部loading状态
|
|
82
|
-
if (footerLoading) {
|
|
83
|
-
footerLoading.value = false
|
|
84
|
-
}
|
|
85
|
-
// 1.请求数据,再关闭
|
|
86
|
-
if (closeFn) {
|
|
87
|
-
closeFn()
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
close: () => {
|
|
91
|
-
// 关闭弹出时立即出发
|
|
92
|
-
console.log('点击关闭')
|
|
93
|
-
// 更新dialogInstances数组
|
|
94
|
-
updateDialogInstances()
|
|
95
|
-
},
|
|
96
|
-
closed: () => {
|
|
97
|
-
// 弹窗销毁时触发
|
|
98
|
-
console.log('完成关闭')
|
|
99
|
-
// 更新dialogInstances数组
|
|
100
|
-
updateDialogInstances()
|
|
101
|
-
},
|
|
102
|
-
// 头部+底部自定义配置
|
|
103
|
-
/*
|
|
104
|
-
// 任何组件添加 $emit('close') 时,会触发关闭弹出框事件
|
|
105
|
-
headerDom: xxx,
|
|
106
|
-
headerOption: {},
|
|
107
|
-
headerEvents: {},
|
|
108
|
-
// 任何组件添加 $emit('close') 时,会触发关闭弹出框事件
|
|
109
|
-
footerDom: yyy,
|
|
110
|
-
footerOption: {},
|
|
111
|
-
footerEvents: {},
|
|
112
|
-
// 底部按钮名称
|
|
113
|
-
footerTitle: {
|
|
114
|
-
close: "取消",
|
|
115
|
-
confirm: "确定",
|
|
116
|
-
},
|
|
117
|
-
*/
|
|
118
|
-
},
|
|
119
|
-
true,
|
|
120
|
-
'#app',
|
|
121
|
-
) // true为是否遮罩(非必填), '#app'为挂载点(非必填)
|
|
122
|
-
|
|
123
|
-
// 更新dialogInstances数组
|
|
124
|
-
updateDialogInstances()
|
|
125
|
-
|
|
126
|
-
setTimeout(()=>{
|
|
127
|
-
const data = window.__dialogInstances;
|
|
128
|
-
console.log('当前所有弹窗实例:', data)
|
|
129
|
-
}, 333)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 更新dialogInstances数组
|
|
133
|
-
function updateDialogInstances() {
|
|
134
|
-
// 使用setTimeout确保在DOM更新后执行
|
|
135
|
-
setTimeout(() => {
|
|
136
|
-
dialogInstances.value = [...window.__dialogInstances]
|
|
137
|
-
}, 0)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// 更新弹窗选项
|
|
141
|
-
function updateDialogOption() {
|
|
142
|
-
if (dialogInstances.value.length > 0) {
|
|
143
|
-
// 更新最后一个弹窗的选项
|
|
144
|
-
const lastInstance = dialogInstances.value[dialogInstances.value.length - 1]
|
|
145
|
-
lastInstance.updateOption({
|
|
146
|
-
params: { test: 'updated_' + Date.now() }
|
|
147
|
-
})
|
|
148
|
-
console.log('已更新弹窗选项')
|
|
149
|
-
} else {
|
|
150
|
-
console.warn('没有打开的弹窗实例')
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// 调用弹窗内组件的方法
|
|
155
|
-
function callDialogMethod() {
|
|
156
|
-
if (dialogInstances.value.length > 0) {
|
|
157
|
-
// 调用最后一个弹窗内组件的方法
|
|
158
|
-
const lastInstance = dialogInstances.value[dialogInstances.value.length - 1]
|
|
159
|
-
if (lastInstance.domRef) {
|
|
160
|
-
// 使用新的callMethod方法调用组件方法
|
|
161
|
-
// 注意:这里需要组件内部通过defineExpose暴露方法
|
|
162
|
-
lastInstance.callMethod('defineExpose暴露的方法名', 'arg1', 'arg2')
|
|
163
|
-
console.log('已调用弹窗内组件方法')
|
|
164
|
-
} else {
|
|
165
|
-
console.warn('组件引用不存在')
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
console.warn('没有打开的弹窗实例')
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// 关闭指定弹窗
|
|
173
|
-
async function closeDialog(instance: any) {
|
|
174
|
-
if (instance) {
|
|
175
|
-
// 使用新的close方法关闭弹窗
|
|
176
|
-
await instance.close()
|
|
177
|
-
console.log('已关闭指定弹窗')
|
|
178
|
-
} else {
|
|
179
|
-
console.warn('弹窗实例不存在')
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 关闭所有弹窗
|
|
184
|
-
function closeAllDialogs() {
|
|
185
|
-
// 关闭所有弹窗
|
|
186
|
-
dialogInstances.value.forEach(instance => {
|
|
187
|
-
instance.close()
|
|
188
|
-
})
|
|
189
|
-
openIndex.value = 0;
|
|
190
|
-
console.log('已关闭所有弹窗')
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
onMounted(() => {
|
|
194
|
-
setTimeout(() => {
|
|
195
|
-
openDialog({ params: { test: '1' } })
|
|
196
|
-
}, 300)
|
|
197
|
-
|
|
198
|
-
// 初始化dialogInstances数组
|
|
199
|
-
updateDialogInstances()
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
onUnmounted(()=>{
|
|
203
|
-
window.__dialogInstances.forEach(item=>{
|
|
204
|
-
item.close()
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
</script>
|
|
208
|
-
<style scoped></style>
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="demo-container">
|
|
3
|
-
<div class="control-panel">
|
|
4
|
-
<h3>Excel 预览演示</h3>
|
|
5
|
-
|
|
6
|
-
<!-- 文件上传方式 -->
|
|
7
|
-
<div class="upload-section">
|
|
8
|
-
<h4>方式一:文件上传</h4>
|
|
9
|
-
<el-form-item label="弹框模式:">
|
|
10
|
-
<el-switch v-model="isShowDialog" @change="hideExcelHandler" />
|
|
11
|
-
</el-form-item>
|
|
12
|
-
<input
|
|
13
|
-
type="file"
|
|
14
|
-
@change="importExcel(($event.target as any)?.files?.[0])"
|
|
15
|
-
accept=".xls,.xlsx"
|
|
16
|
-
/>
|
|
17
|
-
<button @click="clearFile" :disabled="!file">清除文件</button>
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<!-- URL方式 -->
|
|
21
|
-
<div class="url-section">
|
|
22
|
-
<h4>方式二:URL地址</h4>
|
|
23
|
-
<input
|
|
24
|
-
v-model="excelUrl"
|
|
25
|
-
type="text"
|
|
26
|
-
placeholder="请输入Excel文件的URL地址"
|
|
27
|
-
class="url-input"
|
|
28
|
-
/>
|
|
29
|
-
<button @click="loadFromUrl" :disabled="!excelUrl.trim()">加载URL</button>
|
|
30
|
-
<button @click="clearUrl" :disabled="!excelUrl">清除URL</button>
|
|
31
|
-
</div>
|
|
32
|
-
<button @click="exportExcel" class="export-btn">导出数据</button>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
<!-- Excel组件 -->
|
|
36
|
-
<div class="excel-container">
|
|
37
|
-
<NsExcel
|
|
38
|
-
v-if="counts"
|
|
39
|
-
class="excel"
|
|
40
|
-
:file="file"
|
|
41
|
-
:isShowDialog="isShowDialog"
|
|
42
|
-
dialogWidth="1200px"
|
|
43
|
-
dialogHeight="700px"
|
|
44
|
-
ref="excelRef"
|
|
45
|
-
exportType="2"
|
|
46
|
-
@dialogExport="dialogExport"
|
|
47
|
-
></NsExcel>
|
|
48
|
-
</div>
|
|
49
|
-
<!-- 导出数据展示 -->
|
|
50
|
-
<div class="data-display">
|
|
51
|
-
<div class="data-section">
|
|
52
|
-
<h4>导出数据格式1 (原始格式)</h4>
|
|
53
|
-
<pre>{{ data1 }}</pre>
|
|
54
|
-
</div>
|
|
55
|
-
<div class="data-section">
|
|
56
|
-
<h4>导出数据格式2 (Excel格式)</h4>
|
|
57
|
-
<pre>{{ data2 }}</pre>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
</template>
|
|
62
|
-
<script setup lang="ts">
|
|
63
|
-
import { nextTick, ref, watch } from 'vue'
|
|
64
|
-
|
|
65
|
-
const counts = ref(true)
|
|
66
|
-
const isShowDialog = ref(false)
|
|
67
|
-
const file = ref()
|
|
68
|
-
const excelRef = ref()
|
|
69
|
-
const data1 = ref()
|
|
70
|
-
const data2 = ref()
|
|
71
|
-
const excelUrl = ref(
|
|
72
|
-
'https://501351981.github.io/vue-office/examples/dist/static/test-files/test.xlsx',
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
function hideExcelHandler(val: boolean) {
|
|
76
|
-
counts.value = false
|
|
77
|
-
nextTick(() => {
|
|
78
|
-
counts.value = true
|
|
79
|
-
isShowDialog.value = val
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function importExcel(f: any) {
|
|
84
|
-
// 清除URL,使用文件上传
|
|
85
|
-
excelUrl.value = ''
|
|
86
|
-
file.value = f
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function loadFromUrl() {
|
|
90
|
-
if (excelUrl.value.trim()) {
|
|
91
|
-
// 清除文件,使用URL
|
|
92
|
-
file.value = excelUrl.value.trim()
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function clearFile() {
|
|
97
|
-
file.value = null
|
|
98
|
-
data1.value = null
|
|
99
|
-
data2.value = null
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function clearUrl() {
|
|
103
|
-
excelUrl.value = ''
|
|
104
|
-
data1.value = null
|
|
105
|
-
data2.value = null
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function dialogExport(data: any) {
|
|
109
|
-
console.warn(`excel导出数据:${data}`)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function exportExcel() {
|
|
113
|
-
if (excelRef.value) {
|
|
114
|
-
data1.value = excelRef.value.exportExcel(1)
|
|
115
|
-
data2.value = excelRef.value.exportExcel(2)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
</script>
|
|
119
|
-
<style scoped lang="scss">
|
|
120
|
-
.demo-container {
|
|
121
|
-
padding: 20px;
|
|
122
|
-
max-width: 1400px;
|
|
123
|
-
margin: 0 auto;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
.control-panel {
|
|
127
|
-
background: #f5f5f5;
|
|
128
|
-
padding: 20px;
|
|
129
|
-
border-radius: 8px;
|
|
130
|
-
margin-bottom: 20px;
|
|
131
|
-
|
|
132
|
-
h3 {
|
|
133
|
-
margin: 0 0 20px 0;
|
|
134
|
-
color: #333;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
h4 {
|
|
138
|
-
margin: 15px 0 10px 0;
|
|
139
|
-
color: #666;
|
|
140
|
-
font-size: 14px;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.upload-section,
|
|
145
|
-
.url-section,
|
|
146
|
-
.example-section {
|
|
147
|
-
margin-bottom: 20px;
|
|
148
|
-
padding: 15px;
|
|
149
|
-
background: white;
|
|
150
|
-
border-radius: 6px;
|
|
151
|
-
border: 1px solid #e0e0e0;
|
|
152
|
-
|
|
153
|
-
input[type='file'] {
|
|
154
|
-
margin-right: 10px;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.url-input {
|
|
158
|
-
width: 300px;
|
|
159
|
-
padding: 8px 12px;
|
|
160
|
-
border: 1px solid #ddd;
|
|
161
|
-
border-radius: 4px;
|
|
162
|
-
margin-right: 10px;
|
|
163
|
-
font-size: 14px;
|
|
164
|
-
|
|
165
|
-
&:focus {
|
|
166
|
-
outline: none;
|
|
167
|
-
border-color: #409eff;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
button {
|
|
172
|
-
padding: 8px 16px;
|
|
173
|
-
background: #409eff;
|
|
174
|
-
color: white;
|
|
175
|
-
border: none;
|
|
176
|
-
border-radius: 4px;
|
|
177
|
-
cursor: pointer;
|
|
178
|
-
margin-right: 10px;
|
|
179
|
-
font-size: 14px;
|
|
180
|
-
|
|
181
|
-
&:hover:not(:disabled) {
|
|
182
|
-
background: #337ecc;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
&:disabled {
|
|
186
|
-
background: #c0c4cc;
|
|
187
|
-
cursor: not-allowed;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.export-btn {
|
|
193
|
-
padding: 10px 20px;
|
|
194
|
-
background: #67c23a;
|
|
195
|
-
color: white;
|
|
196
|
-
border: none;
|
|
197
|
-
border-radius: 4px;
|
|
198
|
-
cursor: pointer;
|
|
199
|
-
font-size: 16px;
|
|
200
|
-
font-weight: bold;
|
|
201
|
-
|
|
202
|
-
&:hover {
|
|
203
|
-
background: #5daf34;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
.excel {
|
|
208
|
-
width: 100%;
|
|
209
|
-
height: 500px;
|
|
210
|
-
border: 1px solid #e0e0e0;
|
|
211
|
-
border-radius: 8px;
|
|
212
|
-
overflow: hidden;
|
|
213
|
-
margin-bottom: 20px;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.data-display {
|
|
217
|
-
display: flex;
|
|
218
|
-
gap: 20px;
|
|
219
|
-
margin-top: 20px;
|
|
220
|
-
|
|
221
|
-
.data-section {
|
|
222
|
-
flex: 1;
|
|
223
|
-
background: #f9f9f9;
|
|
224
|
-
border-radius: 8px;
|
|
225
|
-
padding: 15px;
|
|
226
|
-
|
|
227
|
-
h4 {
|
|
228
|
-
margin: 0 0 10px 0;
|
|
229
|
-
color: #333;
|
|
230
|
-
font-size: 14px;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
pre {
|
|
234
|
-
background: white;
|
|
235
|
-
padding: 15px;
|
|
236
|
-
border-radius: 4px;
|
|
237
|
-
border: 1px solid #e0e0e0;
|
|
238
|
-
max-height: 300px;
|
|
239
|
-
overflow: auto;
|
|
240
|
-
font-size: 12px;
|
|
241
|
-
line-height: 1.4;
|
|
242
|
-
margin: 0;
|
|
243
|
-
white-space: pre-wrap;
|
|
244
|
-
word-wrap: break-word;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.excel-container {
|
|
250
|
-
height: 300px;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
@media (max-width: 768px) {
|
|
254
|
-
.data-display {
|
|
255
|
-
flex-direction: column;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.url-input {
|
|
259
|
-
width: 100% !important;
|
|
260
|
-
margin-bottom: 10px;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
</style>
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="office-demo">
|
|
3
|
-
<h2>NsOffice 组件演示</h2>
|
|
4
|
-
|
|
5
|
-
<div class="demo-controls">
|
|
6
|
-
<!-- 文件上传方式 -->
|
|
7
|
-
<div class="upload-section">
|
|
8
|
-
<h3>方式一:文件上传</h3>
|
|
9
|
-
<input
|
|
10
|
-
type="file"
|
|
11
|
-
@change="importFile(($event.target as any)?.files?.[0])"
|
|
12
|
-
accept=".docx,.xlsx,.xls,.pdf"
|
|
13
|
-
/>
|
|
14
|
-
<el-button @click="clearFile" :disabled="!file">清除文件</el-button>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<!-- URL方式 -->
|
|
18
|
-
<el-form :model="form" label-width="120px" inline>
|
|
19
|
-
<el-form-item label="文件URL:">
|
|
20
|
-
<el-input v-model="form.url" placeholder="请输入文件URL" style="width: 400px" />
|
|
21
|
-
</el-form-item>
|
|
22
|
-
|
|
23
|
-
<el-form-item label="Excel编辑模式:">
|
|
24
|
-
<el-switch v-model="form.isEdit" />
|
|
25
|
-
</el-form-item>
|
|
26
|
-
|
|
27
|
-
<el-form-item>
|
|
28
|
-
<el-button type="primary" @click="loadFile">加载文件</el-button>
|
|
29
|
-
<el-button @click="clearUrl">清空</el-button>
|
|
30
|
-
</el-form-item>
|
|
31
|
-
</el-form>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
<div class="demo-content">
|
|
35
|
-
<el-card v-if="currentUrl">
|
|
36
|
-
<template #header>
|
|
37
|
-
<div class="card-header">
|
|
38
|
-
<span>当前文件: {{ getFileName(currentUrl) }}</span>
|
|
39
|
-
<span class="file-type">类型: {{ getFileTypeDisplay() }}</span>
|
|
40
|
-
</div>
|
|
41
|
-
</template>
|
|
42
|
-
|
|
43
|
-
<div class="office-container">
|
|
44
|
-
<NsOffice v-bind="form" ref="officeRef" />
|
|
45
|
-
</div>
|
|
46
|
-
</el-card>
|
|
47
|
-
|
|
48
|
-
<el-empty v-else description="请输入文件URL或选择预设文件" />
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</template>
|
|
52
|
-
|
|
53
|
-
<script setup lang="ts">
|
|
54
|
-
import { ref, reactive } from 'vue'
|
|
55
|
-
const form = reactive({
|
|
56
|
-
url: '',
|
|
57
|
-
isEdit: false,
|
|
58
|
-
isShowDialog: false,
|
|
59
|
-
dialogWidth: '1200px',
|
|
60
|
-
dialogHeight: '700px',
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
const currentUrl = ref('')
|
|
64
|
-
const officeRef = ref()
|
|
65
|
-
const file = ref()
|
|
66
|
-
|
|
67
|
-
// 文件上传处理
|
|
68
|
-
function importFile(f: any) {
|
|
69
|
-
// 清除URL,使用文件上传
|
|
70
|
-
form.url = ''
|
|
71
|
-
file.value = f
|
|
72
|
-
|
|
73
|
-
if (f && (f.name.endsWith('.docx') || f.name.endsWith('.xlsx') || f.name.endsWith('.xls') || f.name.endsWith('.pdf'))) {
|
|
74
|
-
// 创建文件URL
|
|
75
|
-
const fileUrl = URL.createObjectURL(f)
|
|
76
|
-
currentUrl.value = fileUrl
|
|
77
|
-
form.url = fileUrl
|
|
78
|
-
} else if (f) {
|
|
79
|
-
alert('请选择支持的文件格式(.docx, .xlsx, .xls, .pdf)')
|
|
80
|
-
clearFile()
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const loadFile = () => {
|
|
85
|
-
if (form.url.trim()) {
|
|
86
|
-
currentUrl.value = form.url.trim()
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const clearFile = () => {
|
|
91
|
-
file.value = null
|
|
92
|
-
if (currentUrl.value.startsWith('blob:')) {
|
|
93
|
-
URL.revokeObjectURL(currentUrl.value)
|
|
94
|
-
}
|
|
95
|
-
currentUrl.value = ''
|
|
96
|
-
form.url = ''
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const clearUrl = () => {
|
|
100
|
-
form.url = ''
|
|
101
|
-
currentUrl.value = ''
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const getFileName = (url: string) => {
|
|
105
|
-
if (!url) return ''
|
|
106
|
-
const parts = url.split('/')
|
|
107
|
-
return parts[parts.length - 1]
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const getFileTypeDisplay = () => {
|
|
111
|
-
if (officeRef.value) {
|
|
112
|
-
const fileType = officeRef.value.getFileType()
|
|
113
|
-
switch (fileType) {
|
|
114
|
-
case 'excel':
|
|
115
|
-
return 'Excel文档'
|
|
116
|
-
case 'pdf':
|
|
117
|
-
return 'PDF文档'
|
|
118
|
-
case 'word':
|
|
119
|
-
return 'Word文档'
|
|
120
|
-
case 'unsupported':
|
|
121
|
-
return '不支持的格式'
|
|
122
|
-
default:
|
|
123
|
-
return '未知'
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return '未知'
|
|
127
|
-
}
|
|
128
|
-
</script>
|
|
129
|
-
|
|
130
|
-
<style scoped>
|
|
131
|
-
.office-demo {
|
|
132
|
-
padding: 20px;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.demo-controls {
|
|
136
|
-
margin-bottom: 20px;
|
|
137
|
-
padding: 20px;
|
|
138
|
-
background: #f5f5f5;
|
|
139
|
-
border-radius: 8px;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.upload-section {
|
|
143
|
-
margin-bottom: 20px;
|
|
144
|
-
padding: 15px;
|
|
145
|
-
background: white;
|
|
146
|
-
border-radius: 6px;
|
|
147
|
-
border: 1px solid #e0e0e0;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.upload-section h3 {
|
|
151
|
-
margin: 0 0 10px 0;
|
|
152
|
-
color: #666;
|
|
153
|
-
font-size: 14px;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.upload-section input[type='file'] {
|
|
157
|
-
margin-right: 10px;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.preset-files {
|
|
161
|
-
margin-top: 20px;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.preset-files h3 {
|
|
165
|
-
margin-bottom: 10px;
|
|
166
|
-
color: #666;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.demo-content {
|
|
170
|
-
min-height: 700px;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.card-header {
|
|
174
|
-
display: flex;
|
|
175
|
-
justify-content: space-between;
|
|
176
|
-
align-items: center;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.file-type {
|
|
180
|
-
color: #409eff;
|
|
181
|
-
font-size: 14px;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.office-container {
|
|
185
|
-
height: 600px;
|
|
186
|
-
border: 1px solid #e4e7ed;
|
|
187
|
-
border-radius: 4px;
|
|
188
|
-
}
|
|
189
|
-
</style>
|