refactor: 代码规范、Bug修复
This commit is contained in:
@@ -1,121 +1,124 @@
|
||||
<!--Yaml编辑器 基于-->
|
||||
<!--<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>,-->
|
||||
<!--主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>-->
|
||||
<!--</p>-->
|
||||
<!-- mode取值如下:yaml、java、go、swift、dockerfile、groovy、lua、perl、python、ruby、sql、xml、vue -->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
|
||||
import 'codemirror/theme/darcula.css'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/mode/yaml/yaml'
|
||||
import 'codemirror/mode/go/go'
|
||||
import 'codemirror/mode/swift/swift'
|
||||
import 'codemirror/mode/dockerfile/dockerfile'
|
||||
import 'codemirror/mode/groovy/groovy'
|
||||
import 'codemirror/mode/lua/lua'
|
||||
import 'codemirror/mode/perl/perl'
|
||||
import 'codemirror/mode/python/python'
|
||||
import 'codemirror/mode/ruby/ruby'
|
||||
import 'codemirror/mode/sql/sql'
|
||||
import 'codemirror/mode/vue/vue'
|
||||
import 'codemirror/mode/xml/xml'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
|
||||
const SupportModeList = ['yaml', 'java', 'go', 'swift', 'dockerfile', 'groovy', 'lua', 'perl', 'python', 'ruby', 'sql', 'xml', 'vue']
|
||||
export default {
|
||||
name: 'CuteCodeEditor',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '500px'
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'yaml'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
content(newVal, oldVal) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (newVal !== editorValue) {
|
||||
this.editor.setValue(newVal)
|
||||
}
|
||||
},
|
||||
height(newVal, oldVal) {
|
||||
this.editor.setSize('auto', newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.mode && SupportModeList.includes(this.mode)) {
|
||||
let mode = 'yaml'
|
||||
if (this.mode === 'java' || this.mode === 'javascript') {
|
||||
mode = 'javascript'
|
||||
} else {
|
||||
mode = this.mode
|
||||
}
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: mode,
|
||||
autoRefresh: true,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 1,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'darcula'
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
if (this.content) {
|
||||
this.editor.setValue(this.content)
|
||||
}
|
||||
this.editor.setOption('readOnly', this.readOnly)
|
||||
this.editor.on('change', cm => {
|
||||
this.$emit('change', cm.getValue())
|
||||
})
|
||||
return
|
||||
}
|
||||
CsMessage.Error('不支持的mode')
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setValue(val) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.setValue(val)
|
||||
},
|
||||
resetField() {
|
||||
this.editor.setValue('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
* 代码编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
* @description 基于Yaml编辑器 https://github.com/codemirror/CodeMirror,主题预览地址 https://codemirror.net/demo/theme.html#idea
|
||||
* @参数mode取值范围 yaml、java、go、swift、dockerfile、groovy、lua、perl、python、ruby、sql、xml、vue
|
||||
-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
|
||||
import 'codemirror/theme/darcula.css'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/mode/yaml/yaml'
|
||||
import 'codemirror/mode/go/go'
|
||||
import 'codemirror/mode/swift/swift'
|
||||
import 'codemirror/mode/dockerfile/dockerfile'
|
||||
import 'codemirror/mode/groovy/groovy'
|
||||
import 'codemirror/mode/lua/lua'
|
||||
import 'codemirror/mode/perl/perl'
|
||||
import 'codemirror/mode/python/python'
|
||||
import 'codemirror/mode/ruby/ruby'
|
||||
import 'codemirror/mode/sql/sql'
|
||||
import 'codemirror/mode/vue/vue'
|
||||
import 'codemirror/mode/xml/xml'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
|
||||
const SupportModeList = ['yaml', 'java', 'go', 'swift', 'dockerfile', 'groovy', 'lua', 'perl', 'python', 'ruby', 'sql', 'xml', 'vue']
|
||||
export default {
|
||||
name: 'CuteCodeEditor',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '500px'
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'yaml'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
content(newVal, oldVal) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (newVal !== editorValue) {
|
||||
this.editor.setValue(newVal)
|
||||
}
|
||||
},
|
||||
height(newVal, oldVal) {
|
||||
this.editor.setSize('auto', newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.mode && SupportModeList.includes(this.mode)) {
|
||||
let mode = 'yaml'
|
||||
if (this.mode === 'java' || this.mode === 'javascript') {
|
||||
mode = 'javascript'
|
||||
} else {
|
||||
mode = this.mode
|
||||
}
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: mode,
|
||||
autoRefresh: true,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 1,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'darcula'
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
if (this.content) {
|
||||
this.editor.setValue(this.content)
|
||||
}
|
||||
this.editor.setOption('readOnly', this.readOnly)
|
||||
this.editor.on('change', cm => {
|
||||
this.$emit('change', cm.getValue())
|
||||
})
|
||||
return
|
||||
}
|
||||
CsMessage.Error('不支持的mode')
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setValue(val) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.setValue(val)
|
||||
},
|
||||
resetField() {
|
||||
this.editor.setValue('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,158 +1,167 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
stripe
|
||||
empty-text="暂无数据"
|
||||
:row-key="crud.primaryKey"
|
||||
fit
|
||||
:data="crud.dataSource"
|
||||
style="width: 100%;"
|
||||
:height="height"
|
||||
:max-height="height"
|
||||
@selection-change="onTableSelectionChange"
|
||||
>
|
||||
<el-table-column v-if="mode === 'multi'" type="selection" width="55" />
|
||||
<slot />
|
||||
<el-pagination
|
||||
:current-page="pageProps.current"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:page-size="pageProps.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pageProps.total"
|
||||
@size-change="(size) => crud.onPageChange(pageProps.current, size, pageProps.total)"
|
||||
@current-change="(current) => crud.onPageChange(current, pageProps.pageSize, pageProps.total)"
|
||||
/>
|
||||
</el-table></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
name: 'CuteDragTable',
|
||||
props: {
|
||||
primaryKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
fetch: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
paramsTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
responseTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'none'
|
||||
},
|
||||
pageProps: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: function() {
|
||||
return {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
crud: {
|
||||
loading: false,
|
||||
primaryKey: this.primaryKey ? this.primaryKey : 'id',
|
||||
fetchUrl: null,
|
||||
dataSource: [],
|
||||
onPageChange: (currentPage, pageSize) => {
|
||||
this.pageProps.current = currentPage
|
||||
this.pageProps.pageSize = pageSize
|
||||
console.log('on-page-change', currentPage, pageSize)
|
||||
// this.$emit('onPageChange', currentPage, pageSize, total)
|
||||
this.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
return document.documentElement.clientHeight - 300
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initData()
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.initData()
|
||||
this.initDragTable()
|
||||
},
|
||||
async initData() {
|
||||
let params = {}
|
||||
// 自定义请求参数
|
||||
if (this.paramsTransform) {
|
||||
params = this.paramsTransform(this.pageProps)
|
||||
}
|
||||
if (this.fetch) {
|
||||
try {
|
||||
this.crud.loading = true
|
||||
// 请求远程数据
|
||||
const response = await this.fetch(params)
|
||||
// console.error('response', response)
|
||||
// { content: [], totalElements: 0 }
|
||||
// 转换为表格所需要的数据
|
||||
if (this.responseTransform) {
|
||||
return this.responseTransform(response)
|
||||
}
|
||||
if (response && response.hasOwnProperty('totalElements') && response.hasOwnProperty('content')) {
|
||||
this.pageProps.total = response.totalElements
|
||||
this.crud.dataSource = response.content
|
||||
}
|
||||
return response
|
||||
} finally {
|
||||
this.crud.loading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
onTableSelectionChange(selection) {
|
||||
this.$emit('selection-change', selection)
|
||||
},
|
||||
initDragTable() {
|
||||
const that = this
|
||||
const table = that.$refs.table.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
|
||||
Sortable.create(table, {
|
||||
animation: 1000,
|
||||
onEnd({ newIndex, oldIndex }) {
|
||||
// 拖拽排序数据
|
||||
that.crud.dataSource.splice(newIndex, 0, that.crud.dataSource.splice(oldIndex, 1)[0])
|
||||
const newArray = that.crud.dataSource.slice(0)
|
||||
that.crud.dataSource = [] // 必须有此步骤,不然拖拽后回弹
|
||||
that.$nextTick(function() {
|
||||
that.crud.dataSource = newArray // 重新赋值,用新数据来刷新视图
|
||||
this.updateOrderNum(that.crud.dataSource)// 更改列表中的序号,使序号1.2.3.4.....显示,不然就是行拖拽后乱序显示如:3.2.4.1...
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
updateOrderNum(data) {
|
||||
data.forEach((item, index) => {
|
||||
item.orderNum = index + 1
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--
|
||||
* 拖拽表格组件:封装自定义逻辑与参数,表格支持拖拽排序
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
stripe
|
||||
empty-text="暂无数据"
|
||||
:row-key="crud.primaryKey"
|
||||
fit
|
||||
:data="crud.dataSource"
|
||||
style="width: 100%;"
|
||||
:height="height"
|
||||
:max-height="height"
|
||||
@selection-change="onTableSelectionChange"
|
||||
>
|
||||
<el-table-column v-if="mode === 'multi'" type="selection" width="55" />
|
||||
<slot />
|
||||
<el-pagination
|
||||
:current-page="pageProps.current"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:page-size="pageProps.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pageProps.total"
|
||||
@size-change="(size) => crud.onPageChange(pageProps.current, size, pageProps.total)"
|
||||
@current-change="(current) => crud.onPageChange(current, pageProps.pageSize, pageProps.total)"
|
||||
/>
|
||||
</el-table></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
name: 'CuteDragTable',
|
||||
props: {
|
||||
primaryKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
fetch: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
paramsTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
responseTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'none'
|
||||
},
|
||||
pageProps: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: function() {
|
||||
return {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
crud: {
|
||||
loading: false,
|
||||
primaryKey: this.primaryKey ? this.primaryKey : 'id',
|
||||
fetchUrl: null,
|
||||
dataSource: [],
|
||||
onPageChange: (currentPage, pageSize) => {
|
||||
this.pageProps.current = currentPage
|
||||
this.pageProps.pageSize = pageSize
|
||||
console.log('on-page-change', currentPage, pageSize)
|
||||
// this.$emit('onPageChange', currentPage, pageSize, total)
|
||||
this.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
return document.documentElement.clientHeight - 300
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initData()
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.initData()
|
||||
this.initDragTable()
|
||||
},
|
||||
async initData() {
|
||||
let params = {}
|
||||
// 自定义请求参数
|
||||
if (this.paramsTransform) {
|
||||
params = this.paramsTransform(this.pageProps)
|
||||
}
|
||||
if (this.fetch) {
|
||||
try {
|
||||
this.crud.loading = true
|
||||
// 请求远程数据
|
||||
const response = await this.fetch(params)
|
||||
// console.error('response', response)
|
||||
// { content: [], totalElements: 0 }
|
||||
// 转换为表格所需要的数据
|
||||
if (this.responseTransform) {
|
||||
return this.responseTransform(response)
|
||||
}
|
||||
if (response && response.hasOwnProperty('totalElements') && response.hasOwnProperty('content')) {
|
||||
this.pageProps.total = response.totalElements
|
||||
this.crud.dataSource = response.content
|
||||
}
|
||||
return response
|
||||
} finally {
|
||||
this.crud.loading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
onTableSelectionChange(selection) {
|
||||
this.$emit('selection-change', selection)
|
||||
},
|
||||
initDragTable() {
|
||||
const that = this
|
||||
const table = that.$refs.table.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
|
||||
Sortable.create(table, {
|
||||
animation: 1000,
|
||||
onEnd({ newIndex, oldIndex }) {
|
||||
// 拖拽排序数据
|
||||
that.crud.dataSource.splice(newIndex, 0, that.crud.dataSource.splice(oldIndex, 1)[0])
|
||||
const newArray = that.crud.dataSource.slice(0)
|
||||
that.crud.dataSource = [] // 必须有此步骤,不然拖拽后回弹
|
||||
that.$nextTick(function() {
|
||||
that.crud.dataSource = newArray // 重新赋值,用新数据来刷新视图
|
||||
this.updateOrderNum(that.crud.dataSource)// 更改列表中的序号,使序号1.2.3.4.....显示,不然就是行拖拽后乱序显示如:3.2.4.1...
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
updateOrderNum(data) {
|
||||
data.forEach((item, index) => {
|
||||
item.orderNum = index + 1
|
||||
})
|
||||
},
|
||||
getDataSource() {
|
||||
return this.crud.dataSource
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,99 +1,105 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:width="width"
|
||||
:fullscreen="fullscreen"
|
||||
:before-close="beforeClose"
|
||||
:modal="false"
|
||||
>
|
||||
<div class="container-form">
|
||||
<el-form ref="form" :model="model" :rules="rules">
|
||||
<!-- 这里是插槽,用于渲染传入的 el-form-item -->
|
||||
<slot />
|
||||
</el-form>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="dialog-footer" style="padding-right: 30px;text-align: right">
|
||||
<el-button type="danger" @click="hidden">取 消</el-button>
|
||||
<!--<el-button @click="resetForm('form')">重 置</el-button>-->
|
||||
<el-button type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteFormDialog',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '40%'
|
||||
},
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
rules: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.visible = true
|
||||
},
|
||||
hidden() {
|
||||
this.resetForm('form')
|
||||
this.visible = false
|
||||
},
|
||||
submitForm(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('submit', this.model)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm(formName) {
|
||||
this.$refs[formName].resetFields()
|
||||
},
|
||||
beforeClose() {
|
||||
this.hidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container-form {
|
||||
overflow-y: scroll; /* 启用滚动 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
padding-right: 20px;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
}
|
||||
|
||||
.container-form::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera*/
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
* 简单表单弹窗:封装了常用方法和参数
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:width="width"
|
||||
:fullscreen="fullscreen"
|
||||
:before-close="beforeClose"
|
||||
:modal="false"
|
||||
>
|
||||
<div class="container-form">
|
||||
<el-form ref="form" :model="model" :rules="rules">
|
||||
<!-- 这里是插槽,用于渲染传入的 el-form-item -->
|
||||
<slot />
|
||||
</el-form>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="dialog-footer" style="padding-right: 30px;text-align: right">
|
||||
<el-button type="danger" @click="hidden">取 消</el-button>
|
||||
<!--<el-button @click="resetForm('form')">重 置</el-button>-->
|
||||
<el-button v-prevent-re-click type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteFormDialog',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '40%'
|
||||
},
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
rules: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.visible = true
|
||||
},
|
||||
hidden() {
|
||||
this.resetForm('form')
|
||||
this.visible = false
|
||||
},
|
||||
submitForm(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('submit', this.model)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm(formName) {
|
||||
this.$refs[formName].resetFields()
|
||||
},
|
||||
beforeClose() {
|
||||
this.hidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container-form {
|
||||
overflow-y: scroll; /* 启用滚动 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
padding-right: 20px;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
}
|
||||
|
||||
.container-form::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera*/
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,97 +1,104 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:size="width"
|
||||
:before-close="beforeClose"
|
||||
:append-to-body="true"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:show-close="false"
|
||||
:modal="false"
|
||||
>
|
||||
<div class="container-form">
|
||||
<el-form ref="form" :model="model" :rules="rules">
|
||||
<!-- 这里是插槽,用于渲染传入的 el-form-item -->
|
||||
<slot />
|
||||
</el-form>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="dialog-footer" style="padding-right: 30px;text-align: right">
|
||||
<el-button type="danger" @click="hidden">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteFormDrawer',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '40%'
|
||||
},
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
rules: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.visible = true
|
||||
},
|
||||
hidden() {
|
||||
this.resetForm('form')
|
||||
this.visible = false
|
||||
},
|
||||
submitForm(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('submit', this.model)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm(formName) {
|
||||
this.$refs[formName].resetFields()
|
||||
},
|
||||
beforeClose() {
|
||||
this.hidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container-form {
|
||||
overflow-y: scroll; /* 启用滚动 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
height: 85%;
|
||||
padding-right: 20px;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
}
|
||||
|
||||
.container-form::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera*/
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
* 简单表单抽屉:封装了常用方法和参数
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-drawer
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:size="width"
|
||||
:before-close="beforeClose"
|
||||
:append-to-body="true"
|
||||
:close-on-press-escape="false"
|
||||
:wrapper-closable="false"
|
||||
:destroy-on-close="true"
|
||||
:show-close="true"
|
||||
:modal="false"
|
||||
>
|
||||
<div class="container-form">
|
||||
<el-form ref="form" :model="model" :rules="rules">
|
||||
<!-- 这里是插槽,用于渲染传入的 el-form-item -->
|
||||
<slot />
|
||||
</el-form>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="dialog-footer" style="padding-right: 30px;text-align: right">
|
||||
<el-button type="danger" @click="hidden">取 消</el-button>
|
||||
<el-button v-prevent-re-click type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteFormDrawer',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '40%'
|
||||
},
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
rules: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.visible = true
|
||||
},
|
||||
hidden() {
|
||||
this.resetForm('form')
|
||||
this.visible = false
|
||||
},
|
||||
submitForm(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('submit', this.model)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm(formName) {
|
||||
this.$refs[formName].resetFields()
|
||||
},
|
||||
beforeClose() {
|
||||
this.hidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container-form {
|
||||
overflow-y: scroll; /* 启用滚动 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
height: 85%;
|
||||
padding-right: 20px;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
}
|
||||
|
||||
.container-form::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera*/
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<!--
|
||||
* 横向滚动条容器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<div class="overflow-panel">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteHorizontalScrollBar'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.overflow-panel {
|
||||
width: 1500px; /* 固定宽度 */
|
||||
overflow-x: auto; /* 允许水平滚动 */
|
||||
white-space: nowrap; /* 防止内容换行,使所有内容在一行显示 */
|
||||
padding: 30px 20px 30px 20px;
|
||||
}
|
||||
|
||||
.overflow-panel::-webkit-scrollbar {
|
||||
height: 5px; /* 设置水平滚动条高度 */
|
||||
}
|
||||
|
||||
.overflow-panel::-webkit-scrollbar-track {
|
||||
background: #f1f1f1; /* 滚动条底色 */
|
||||
}
|
||||
|
||||
.overflow-panel::-webkit-scrollbar-thumb {
|
||||
background: #DCDFE6; /* 滚动条颜色 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,91 +1,93 @@
|
||||
<!--Yaml编辑器 基于-->
|
||||
<!--<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>,-->
|
||||
<!--主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>-->
|
||||
<!--</p>-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
|
||||
import 'codemirror/theme/darcula.css'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
|
||||
export default {
|
||||
name: 'CuteJavaEditor',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '500px'
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
content(newVal, oldVal) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (newVal !== editorValue) {
|
||||
this.editor.setValue(newVal)
|
||||
}
|
||||
},
|
||||
height(newVal, oldVal) {
|
||||
this.editor.setSize('auto', newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: 'javascript',
|
||||
autoRefresh: true,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 1,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'darcula'
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
if (this.content) {
|
||||
this.editor.setValue(this.content)
|
||||
}
|
||||
this.editor.setOption('readOnly', this.readOnly)
|
||||
this.editor.on('change', cm => {
|
||||
this.$emit('change', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setValue(val) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.setValue(val)
|
||||
},
|
||||
resetField() {
|
||||
this.editor.setValue('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
* Java代码编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
|
||||
import 'codemirror/theme/darcula.css'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
|
||||
export default {
|
||||
name: 'CuteJavaEditor',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '500px'
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
content(newVal, oldVal) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (newVal !== editorValue) {
|
||||
this.editor.setValue(newVal)
|
||||
}
|
||||
},
|
||||
height(newVal, oldVal) {
|
||||
this.editor.setSize('auto', newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: 'javascript',
|
||||
autoRefresh: true,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 1,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'darcula'
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
if (this.content) {
|
||||
this.editor.setValue(this.content)
|
||||
}
|
||||
this.editor.setOption('readOnly', this.readOnly)
|
||||
this.editor.on('change', cm => {
|
||||
this.$emit('change', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setValue(val) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.setValue(val)
|
||||
},
|
||||
resetField() {
|
||||
this.editor.setValue('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
* 文字超链接
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-26
|
||||
-->
|
||||
<template>
|
||||
<el-link
|
||||
type="primary"
|
||||
:underline="false"
|
||||
:href="href"
|
||||
target="_blank"
|
||||
>{{ title }}</el-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteLink',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
href: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'https://github.com/odboy-tianjun'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,76 +1,83 @@
|
||||
<!--<p class="warn-content">-->
|
||||
<!--Markdown 基于-->
|
||||
<!--<el-link type="primary" href="https://github.com/hinesboy/mavonEditor" target="_blank">MavonEditor</el-link>-->
|
||||
<!--</p>-->
|
||||
<template>
|
||||
<mavon-editor
|
||||
ref="md"
|
||||
v-model="content"
|
||||
:style="`height: + ${height};overflow-y: hidden`"
|
||||
:ishljs="true"
|
||||
@imgAdd="imgAdd"
|
||||
@change="onChange"
|
||||
@save="onSave"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mavonEditor } from 'mavon-editor'
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
import { UploadFile } from '@/utils/CsDomUtil'
|
||||
|
||||
const dynamicHeight = 160
|
||||
export default {
|
||||
name: 'CuteMarkdown',
|
||||
components: {
|
||||
mavonEditor
|
||||
},
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
height: document.documentElement.clientHeight - dynamicHeight + 'px'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'imagesUploadApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
mounted() {
|
||||
const that = this
|
||||
window.onresize = () => {
|
||||
that.height = document.documentElement.clientHeight - dynamicHeight + 'px'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
imgAdd(pos, $file) {
|
||||
const that = this
|
||||
UploadFile(that.imagesUploadApi, $file).then(res => {
|
||||
const data = res.data
|
||||
const url = that.baseApi + '/file/' + data.type + '/' + data.realName
|
||||
that.$refs.md.$img2Url(pos, url)
|
||||
})
|
||||
},
|
||||
onChange(value, render) {
|
||||
this.$emit('change', value, render)
|
||||
},
|
||||
onSave(value, render) {
|
||||
this.$emit('save', value, render)
|
||||
},
|
||||
resetField() {
|
||||
this.content = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<!--<p class="warn-content">-->
|
||||
<!--Markdown 基于-->
|
||||
<!--<el-link type="primary" href="" target="_blank">MavonEditor</el-link>-->
|
||||
<!--</p>-->
|
||||
<!--
|
||||
* Markdown编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
* @description 基于MavonEditor https://github.com/hinesboy/mavonEditor
|
||||
-->
|
||||
<template>
|
||||
<mavon-editor
|
||||
ref="md"
|
||||
v-model="content"
|
||||
:style="`height: + ${height};overflow-y: hidden`"
|
||||
:ishljs="true"
|
||||
@imgAdd="imgAdd"
|
||||
@change="onChange"
|
||||
@save="onSave"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mavonEditor } from 'mavon-editor'
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
import { UploadFile } from '@/utils/CsDomUtil'
|
||||
|
||||
const dynamicHeight = 160
|
||||
export default {
|
||||
name: 'CuteMarkdown',
|
||||
components: {
|
||||
mavonEditor
|
||||
},
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
height: document.documentElement.clientHeight - dynamicHeight + 'px'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'imagesUploadApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
mounted() {
|
||||
const that = this
|
||||
window.onresize = () => {
|
||||
that.height = document.documentElement.clientHeight - dynamicHeight + 'px'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
imgAdd(pos, $file) {
|
||||
const that = this
|
||||
UploadFile(that.imagesUploadApi, $file).then(res => {
|
||||
const data = res.data
|
||||
const url = that.baseApi + '/file/' + data.type + '/' + data.realName
|
||||
that.$refs.md.$img2Url(pos, url)
|
||||
})
|
||||
},
|
||||
onChange(value, render) {
|
||||
this.$emit('change', value, render)
|
||||
},
|
||||
onSave(value, render) {
|
||||
this.$emit('save', value, render)
|
||||
},
|
||||
resetField() {
|
||||
this.content = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* 无数据
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<el-empty :description="desc" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteNoData',
|
||||
props: {
|
||||
desc: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '暂无匹配数据'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,44 +1,49 @@
|
||||
<!-- 一键复制组件 -->
|
||||
<!-- 使用方式: <one-key-copy style="margin-left: 10pt" :content.sync="data" /> -->
|
||||
<template>
|
||||
<el-button
|
||||
v-clipboard:copy="content"
|
||||
v-clipboard:success="onCopy"
|
||||
v-clipboard:error="onError"
|
||||
size="mini"
|
||||
icon="el-icon-document-copy"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CuteOneKeyCopy',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCopy(row) {
|
||||
const h = this.$createElement
|
||||
this.$notify({
|
||||
title: row.text + ' 复制成功',
|
||||
message: h('i', { style: 'color: teal' }, '已复制到剪贴板,请直接去 Ctrl+V 粘贴吧!')
|
||||
})
|
||||
},
|
||||
// 复制失败
|
||||
onError(row) {
|
||||
const h = this.$createElement
|
||||
this.$notify({
|
||||
title: row.text + ' 复制失败',
|
||||
message: h('i', { style: 'color: teal' }, '联系技术吧!')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<!--
|
||||
* 一键复制组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
* @description 使用方式:<cute-one-key-copy style="margin-left: 10pt" :content.sync="data" />
|
||||
-->
|
||||
<template>
|
||||
<el-button
|
||||
v-clipboard:copy="content"
|
||||
v-clipboard:success="onCopy"
|
||||
v-clipboard:error="onError"
|
||||
size="mini"
|
||||
icon="el-icon-document-copy"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CuteOneKeyCopy',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCopy(row) {
|
||||
const h = this.$createElement
|
||||
this.$notify({
|
||||
title: row.text + ' 复制成功',
|
||||
message: h('i', { style: 'color: teal' }, '已复制到剪贴板,请直接去 Ctrl+V 粘贴吧!')
|
||||
})
|
||||
},
|
||||
// 复制失败
|
||||
onError(row) {
|
||||
const h = this.$createElement
|
||||
this.$notify({
|
||||
title: row.text + ' 复制失败',
|
||||
message: h('i', { style: 'color: teal' }, '联系技术吧!')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="box-card"
|
||||
:style="cardStyle"
|
||||
>
|
||||
<div>
|
||||
<div style="float: left;">
|
||||
<i :class="statusIconClass" :style="statusIconStyle" />
|
||||
</div>
|
||||
<div class="box-name">{{ currentTemplateData.name }}</div>
|
||||
<div style="clear: both" />
|
||||
</div>
|
||||
<div class="box-current-node">
|
||||
<a @click="onCurrentNodeClick">
|
||||
{{ currentNodeDisplayText }}
|
||||
</a>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :span="12" style="text-align: left">
|
||||
<!-- 流水线节点状态为fail,且节点支持重试 -->
|
||||
<div v-if="showRetryButton">
|
||||
<div class="box-buttons">
|
||||
<el-button
|
||||
v-prevent-re-click="5000"
|
||||
type="text"
|
||||
size="medium"
|
||||
:style="getButtonStyle()"
|
||||
@click="onNodeRetryClick"
|
||||
>
|
||||
重试
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 流水线节点状态为success,且有操作按钮 -->
|
||||
<div v-else-if="showOperationButtons">
|
||||
<div class="box-buttons">
|
||||
<el-button
|
||||
v-for="buttonItem in currentTemplateData.buttons"
|
||||
:key="buttonItem.service"
|
||||
size="medium"
|
||||
type="text"
|
||||
:style="getButtonStyle(buttonItem)"
|
||||
@click="onNodeOperationClick(buttonItem)"
|
||||
>
|
||||
{{ buttonItem.title }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 流水线节点状态为running,且有操作按钮 -->
|
||||
<div v-else-if="showOperationButtons">
|
||||
<div class="box-buttons">
|
||||
<el-button
|
||||
v-for="buttonItem in currentTemplateData.buttons"
|
||||
:key="buttonItem.service"
|
||||
size="medium"
|
||||
type="text"
|
||||
:style="getButtonStyle(buttonItem)"
|
||||
@click="onNodeOperationClick(buttonItem)"
|
||||
>
|
||||
{{ buttonItem.title }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 占位用,为了前端样式美观些 -->
|
||||
<div v-else>
|
||||
<div style="width: 110px;height: 30px" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="box-execute-time">
|
||||
{{ executeTimeStr }}
|
||||
</div>
|
||||
</el-col>
|
||||
<div style="clear: both" />
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dayjs from 'dayjs'
|
||||
import { mapGetters } from 'vuex'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
export default {
|
||||
name: 'CutePipelineNode',
|
||||
props: {
|
||||
templateData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: function() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
instanceData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: function() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentTemplateData: {},
|
||||
currentInstanceData: {}
|
||||
}
|
||||
},
|
||||
// 动态计算
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'statusConst'
|
||||
]),
|
||||
cardStyle() {
|
||||
const statusKey = this.currentTemplateData.currentNodeStatus || this.statusConst.pending.code
|
||||
const statusConfig = this.statusConst[statusKey] || this.statusConst.pending
|
||||
return {
|
||||
borderTop: `5px solid ${statusConfig.color}`
|
||||
}
|
||||
},
|
||||
statusIconClass() {
|
||||
const status = this.currentTemplateData.currentNodeStatus || this.statusConst.pending.code
|
||||
return this.statusConst[status].icon || 'el-icon-time'
|
||||
},
|
||||
statusIconStyle() {
|
||||
return {
|
||||
fontSize: '22px',
|
||||
color: this.getStatusColor()
|
||||
}
|
||||
},
|
||||
currentNodeDisplayText() {
|
||||
return this.currentTemplateData.currentNodeMsg || this.statusConst.pending.label
|
||||
},
|
||||
showRetryButton() {
|
||||
return this.currentTemplateData.currentNodeStatus === this.statusConst.fail.code &&
|
||||
this.currentTemplateData.retry === true
|
||||
},
|
||||
showOperationButtons() {
|
||||
const status = this.currentTemplateData.currentNodeStatus
|
||||
const hasButtons = this.currentTemplateData.buttons && this.currentTemplateData.buttons.length > 0
|
||||
return hasButtons && (
|
||||
status === this.statusConst.success.code ||
|
||||
status === this.statusConst.running.code
|
||||
)
|
||||
},
|
||||
executeTimeStr() {
|
||||
return this.renderDateTimeStr(this.currentTemplateData)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
templateData: {
|
||||
handler(newVal, oldVal) {
|
||||
this.currentTemplateData = { ...newVal }
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
instanceData: {
|
||||
handler(newVal, oldVal) {
|
||||
this.currentInstanceData = { ...newVal }
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化时,设置流水线模板
|
||||
this.currentTemplateData = this.templateData
|
||||
this.currentInstanceData = this.instanceData
|
||||
},
|
||||
methods: {
|
||||
getStatusColor() {
|
||||
const status = this.currentTemplateData.currentNodeStatus || this.statusConst.pending.code
|
||||
const statusConfig = this.statusConst[status] || this.statusConst.pending
|
||||
return statusConfig.color
|
||||
},
|
||||
getButtonStyle(buttonItem) {
|
||||
if (buttonItem) {
|
||||
const isPositiveAction = ['apply', 'agree', 'ok', 'success'].includes(buttonItem.code)
|
||||
const status = this.currentTemplateData.currentNodeStatus
|
||||
const color = isPositiveAction
|
||||
? this.statusConst.running.color
|
||||
: this.statusConst.fail.color
|
||||
let display = ''
|
||||
if (status === this.statusConst.running.code) {
|
||||
display = this.getRunningNodeButtonVisibleStatus(buttonItem)
|
||||
} else if (status === this.statusConst.success.code) {
|
||||
display = this.getSuccessNodeButtonVisibleStatus(buttonItem)
|
||||
}
|
||||
return {
|
||||
padding: 0,
|
||||
margin: '0 10px 0 10px',
|
||||
color,
|
||||
display
|
||||
}
|
||||
}
|
||||
return {
|
||||
padding: 0,
|
||||
margin: '0 10px 0 10px',
|
||||
color: this.statusConst.running.color
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 运行中节点的按钮显示
|
||||
* @param buttonItem
|
||||
* @returns {string}
|
||||
*/
|
||||
getRunningNodeButtonVisibleStatus(buttonItem) {
|
||||
if (buttonItem.type === 'qrCodeDialog') {
|
||||
return 'none'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
/**
|
||||
* 成功节点的按钮显示
|
||||
* @param buttonItem
|
||||
* @returns {string}
|
||||
*/
|
||||
getSuccessNodeButtonVisibleStatus(buttonItem) {
|
||||
const that = this
|
||||
// 根据按钮编码
|
||||
if (buttonItem.code === that.currentTemplateData.currentNodeStatus) {
|
||||
return ''
|
||||
}
|
||||
return 'none'
|
||||
},
|
||||
renderDateTimeStr(newVal) {
|
||||
let executeTimeStr = ''
|
||||
switch (newVal.status) {
|
||||
case 'pending':
|
||||
executeTimeStr = ''
|
||||
break
|
||||
case 'running':
|
||||
case 'success':
|
||||
case 'fail':
|
||||
if (newVal.currentNodeStatus !== this.statusConst.pending.code) {
|
||||
if (newVal.startTime && newVal.finishTime) {
|
||||
executeTimeStr = this.formatTimeDifference(newVal.startTime, newVal.finishTime)
|
||||
return executeTimeStr
|
||||
}
|
||||
if (newVal.startTime) {
|
||||
executeTimeStr = this.formatTimeDifference(newVal.startTime, new Date())
|
||||
return executeTimeStr
|
||||
}
|
||||
if (newVal.createTime) {
|
||||
executeTimeStr = this.formatTimeDifference(newVal.createTime, new Date())
|
||||
return executeTimeStr
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
executeTimeStr = ''
|
||||
break
|
||||
}
|
||||
return executeTimeStr
|
||||
},
|
||||
formatTimeDifference(createTimeStr, updateTimeStr) {
|
||||
const startTime = dayjs(createTimeStr)
|
||||
const endTime = dayjs(updateTimeStr)
|
||||
// 计算两个时间点之间的差异(毫秒)
|
||||
const diffInMilliseconds = endTime.diff(startTime)
|
||||
// 将毫秒转换为秒
|
||||
let totalSeconds = Math.floor(diffInMilliseconds / 1000)
|
||||
const days = Math.floor(totalSeconds / (3600 * 24))
|
||||
totalSeconds %= 3600 * 24
|
||||
const hours = Math.floor(totalSeconds / 3600)
|
||||
totalSeconds %= 3600
|
||||
const minutes = Math.floor(totalSeconds / 60)
|
||||
const seconds = totalSeconds % 60
|
||||
// 格式化为字符串
|
||||
if (days > 0) {
|
||||
return `${days} 天 ${hours} 时 ${minutes} 分 ${seconds} 秒`
|
||||
}
|
||||
if (hours > 0) {
|
||||
return `${hours} 时 ${minutes} 分 ${seconds} 秒`
|
||||
}
|
||||
if (minutes > 0) {
|
||||
return `${minutes} 分 ${seconds} 秒`
|
||||
}
|
||||
if (seconds >= 0) {
|
||||
return `${seconds} 秒`
|
||||
}
|
||||
return `${diffInMilliseconds} 毫秒`
|
||||
},
|
||||
onCurrentNodeClick() {
|
||||
const currentTemplateData = this.currentTemplateData || { click: false }
|
||||
if (!currentTemplateData.click) {
|
||||
CsMessage.Warning('当前流水线节点不支持查看明细')
|
||||
return
|
||||
}
|
||||
this.$emit('node-click', this.templateData)
|
||||
},
|
||||
onNodeRetryClick() {
|
||||
const currentTemplateData = this.currentTemplateData || { retry: false }
|
||||
if (!currentTemplateData.retry) {
|
||||
CsMessage.Warning('当前流水线节点不支持重试')
|
||||
return
|
||||
}
|
||||
const data = {
|
||||
...this.currentInstanceData,
|
||||
code: this.currentTemplateData.code
|
||||
}
|
||||
this.$emit('node-retry', data)
|
||||
},
|
||||
onNodeOperationClick(buttonInfo) {
|
||||
const currentTemplateData = this.currentTemplateData || {}
|
||||
this.$emit('node-opera', currentTemplateData, buttonInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.box-card {
|
||||
width: 300px; /* 卡片宽度 */
|
||||
max-width: 300px; /* 卡片宽度 */
|
||||
padding: 10px;
|
||||
background-color: #fff; /* 背景颜色 */
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
border-radius: 8px; /* 圆角效果 */
|
||||
overflow: hidden; /* 防止子元素溢出 */
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease; /* 过渡效果 */
|
||||
display: inline-block;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.box-card:hover {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.box-name {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
font-size: 16px;
|
||||
line-height: 22px
|
||||
}
|
||||
|
||||
.box-current-node {
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
font-size: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.box-buttons {
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.box-execute-time {
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,65 +1,77 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:size="width"
|
||||
:before-close="beforeClose"
|
||||
:append-to-body="true"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:show-close="false"
|
||||
:modal="false"
|
||||
>
|
||||
<!-- 这里是插槽,用于渲染传入的组件 -->
|
||||
<slot />
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CutePreviewDrawer',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '40%'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.visible = true
|
||||
},
|
||||
hidden() {
|
||||
this.visible = false
|
||||
},
|
||||
beforeClose() {
|
||||
this.hidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container-form {
|
||||
overflow-y: scroll; /* 启用滚动 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
height: 85%;
|
||||
padding-right: 20px;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
}
|
||||
|
||||
.container-form::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera*/
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
* 预览抽屉:封装了常用的方法和属性
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-drawer
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:size="width"
|
||||
:before-close="beforeClose"
|
||||
:append-to-body="true"
|
||||
:close-on-press-escape="false"
|
||||
:wrapper-closable="wrapperClosable"
|
||||
:destroy-on-close="true"
|
||||
:show-close="true"
|
||||
:modal="false"
|
||||
>
|
||||
<!-- 这里是插槽,用于渲染传入的组件 -->
|
||||
<slot />
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CutePreviewDrawer',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '40%'
|
||||
},
|
||||
wrapperClosable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.visible = true
|
||||
},
|
||||
hidden() {
|
||||
this.visible = false
|
||||
},
|
||||
beforeClose() {
|
||||
this.hidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container-form {
|
||||
overflow-y: scroll; /* 启用滚动 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
height: 85%;
|
||||
padding-right: 20px;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
}
|
||||
|
||||
.container-form::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera*/
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,298 +1,302 @@
|
||||
<!--富文本基于-->
|
||||
<!--<el-link type="primary" href="https://www.wangeditor.com/v5/getting-started.html" target="_blank">wangEditor</el-link>-->
|
||||
<template>
|
||||
<div ref="editor" style="border: 1px solid #ccc;">
|
||||
<Toolbar
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editor"
|
||||
:default-config="toolbarConfig"
|
||||
:mode="editMode"
|
||||
/>
|
||||
<Editor
|
||||
v-model="editValue"
|
||||
:style="`height: 500px;overflow-y: hidden`"
|
||||
:default-config="editorConfig"
|
||||
:mode="editMode"
|
||||
@onCreated="onCreated"
|
||||
@onChange="onChange"
|
||||
@onDestroyed="onDestroyed"
|
||||
@onFocus="onFocus"
|
||||
@onBlur="onBlur"
|
||||
@customAlert="customAlert"
|
||||
@customPaste="customPaste"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import { UploadFile } from '@/utils/CsDomUtil'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
|
||||
export default {
|
||||
name: 'CuteRichTextEditor',
|
||||
components: { Toolbar, Editor },
|
||||
props: {
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const that = this
|
||||
return {
|
||||
toolbarConfig: {},
|
||||
editorConfig: {
|
||||
placeholder: '请输入内容...',
|
||||
readOnly: that.readOnly,
|
||||
autoFocus: true,
|
||||
scroll: false,
|
||||
// 会造成卡顿
|
||||
// maxLength: this.maxLength
|
||||
MENU_CONF: {
|
||||
'uploadImage': {
|
||||
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
|
||||
allowedFileTypes: ['image/*'],
|
||||
// 自定义上传
|
||||
async customUpload(file, insertFn) { // JS 语法
|
||||
UploadFile(that.imagesUploadApi, file).then(res => {
|
||||
const data = res.data
|
||||
const url = that.baseApi + '/file/' + data.dateGroup + '/' + data.realName
|
||||
// 最后插入图片
|
||||
insertFn(url, '', '')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 'default' or 'simple'
|
||||
editMode: 'simple',
|
||||
editor: null,
|
||||
editValue: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'imagesUploadApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 编辑器创建完毕时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onCreated(editor) {
|
||||
// 一定要用 Object.seal(), 否则会报错
|
||||
this.editor = Object.seal(editor)
|
||||
this.editValue = this.content
|
||||
},
|
||||
/**
|
||||
* 编辑器内容、选区变化时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onChange(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
// 非格式化的 html
|
||||
const html = editor.getHtml()
|
||||
// 纯文本内容
|
||||
const text = editor.getText()
|
||||
this.$emit('change', html, text)
|
||||
},
|
||||
/**
|
||||
* 编辑器销毁时的回调函数。调用 editor.destroy() 即可销毁编辑器
|
||||
* @param editor
|
||||
*/
|
||||
onDestroyed(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
editor.destroy()
|
||||
},
|
||||
/**
|
||||
* 编辑器内容达到最大长度时的回调函数(会导致卡顿)
|
||||
* @param editor
|
||||
*/
|
||||
onMaxLength(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 编辑器 focus 时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onFocus(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
this.$emit('focus', editor)
|
||||
},
|
||||
/**
|
||||
* 编辑器 blur 时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onBlur(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
this.$emit('blur', editor)
|
||||
},
|
||||
/**
|
||||
* 自定义编辑器 alert 。如想用 ElementUI 的 Message 功能
|
||||
* @param editor
|
||||
*/
|
||||
customAlert(info, type) {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
CsMessage.Error(info)
|
||||
break
|
||||
case 'info':
|
||||
CsMessage.Info(info)
|
||||
break
|
||||
case 'warning':
|
||||
CsMessage.Warning(info)
|
||||
break
|
||||
case 'error':
|
||||
CsMessage.Error(info)
|
||||
break
|
||||
default:
|
||||
CsMessage.Info(info)
|
||||
break
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑
|
||||
* @param editor
|
||||
*/
|
||||
customPaste(editor, event, callback) {
|
||||
if (editor == null) {
|
||||
callback(true)
|
||||
}
|
||||
// console.log('ClipboardEvent 粘贴事件对象', event)
|
||||
// // const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
|
||||
// // const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
|
||||
// // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
|
||||
//
|
||||
// // 自定义插入内容
|
||||
// editor.insertText('xxx')
|
||||
//
|
||||
// // 返回 false ,阻止默认粘贴行为
|
||||
// event.preventDefault()
|
||||
// callback(false) // 返回值(注意,vue 事件的返回值,不能用 return)
|
||||
//
|
||||
// // 返回 true ,继续默认的粘贴行为
|
||||
// // callback(true)
|
||||
callback(true)
|
||||
},
|
||||
/**
|
||||
* 设置编辑器html内容
|
||||
* @param html
|
||||
* <br/>
|
||||
* 如果是 editor.getHtml() 获取的 HTML 格式,可以完美解析。
|
||||
* 如果是其他的 HTML 格式,则不能保证语义正确 —— dangerously 。
|
||||
*/
|
||||
setHtml(html) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.dangerouslyInsertHtml(html)
|
||||
},
|
||||
/**
|
||||
* 获取编辑器html内容
|
||||
*/
|
||||
getHtml() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
this.editor.getHtml()
|
||||
},
|
||||
/**
|
||||
* 清空编辑器内容
|
||||
*/
|
||||
clear() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.clear()
|
||||
},
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
undo() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.undo()
|
||||
},
|
||||
/**
|
||||
* 重做
|
||||
*/
|
||||
redo() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.redo()
|
||||
},
|
||||
/**
|
||||
* 获取选中的文本
|
||||
*/
|
||||
getSelectionText() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getSelectionText()
|
||||
},
|
||||
/**
|
||||
* 删除选中的内容
|
||||
*/
|
||||
deleteFragment() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.deleteFragment()
|
||||
},
|
||||
/**
|
||||
* 获取选中的内容。JSON格式
|
||||
*/
|
||||
getFragment() {
|
||||
if (this.editor == null) {
|
||||
return {}
|
||||
}
|
||||
return this.editor.getFragment()
|
||||
},
|
||||
/**
|
||||
* 获取文本内容
|
||||
*/
|
||||
getText() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getText()
|
||||
},
|
||||
/**
|
||||
* 在选区插入文本
|
||||
* @param text
|
||||
*/
|
||||
insertText(text) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.insertText(text)
|
||||
},
|
||||
resetField() {
|
||||
this.setHtml('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
* 富文本编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<div ref="editor" style="border: 1px solid #ccc;">
|
||||
<Toolbar
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editor"
|
||||
:default-config="toolbarConfig"
|
||||
:mode="editMode"
|
||||
/>
|
||||
<Editor
|
||||
v-model="editValue"
|
||||
:style="`height: 500px;overflow-y: hidden`"
|
||||
:default-config="editorConfig"
|
||||
:mode="editMode"
|
||||
@onCreated="onCreated"
|
||||
@onChange="onChange"
|
||||
@onDestroyed="onDestroyed"
|
||||
@onFocus="onFocus"
|
||||
@onBlur="onBlur"
|
||||
@customAlert="customAlert"
|
||||
@customPaste="customPaste"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import { UploadFile } from '@/utils/CsDomUtil'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
|
||||
export default {
|
||||
name: 'CuteRichTextEditor',
|
||||
components: { Toolbar, Editor },
|
||||
props: {
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const that = this
|
||||
return {
|
||||
toolbarConfig: {},
|
||||
editorConfig: {
|
||||
placeholder: '请输入内容...',
|
||||
readOnly: that.readOnly,
|
||||
autoFocus: true,
|
||||
scroll: false,
|
||||
// 会造成卡顿
|
||||
// maxLength: this.maxLength
|
||||
MENU_CONF: {
|
||||
'uploadImage': {
|
||||
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
|
||||
allowedFileTypes: ['image/*'],
|
||||
// 自定义上传
|
||||
async customUpload(file, insertFn) { // JS 语法
|
||||
UploadFile(that.imagesUploadApi, file).then(res => {
|
||||
const data = res.data
|
||||
const url = that.baseApi + '/file/' + data.dateGroup + '/' + data.realName
|
||||
// 最后插入图片
|
||||
insertFn(url, '', '')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 'default' or 'simple'
|
||||
editMode: 'simple',
|
||||
editor: null,
|
||||
editValue: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'imagesUploadApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 编辑器创建完毕时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onCreated(editor) {
|
||||
// 一定要用 Object.seal(), 否则会报错
|
||||
this.editor = Object.seal(editor)
|
||||
this.editValue = this.content
|
||||
},
|
||||
/**
|
||||
* 编辑器内容、选区变化时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onChange(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
// 非格式化的 html
|
||||
const html = editor.getHtml()
|
||||
// 纯文本内容
|
||||
const text = editor.getText()
|
||||
this.$emit('change', html, text)
|
||||
},
|
||||
/**
|
||||
* 编辑器销毁时的回调函数。调用 editor.destroy() 即可销毁编辑器
|
||||
* @param editor
|
||||
*/
|
||||
onDestroyed(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
editor.destroy()
|
||||
},
|
||||
/**
|
||||
* 编辑器内容达到最大长度时的回调函数(会导致卡顿)
|
||||
* @param editor
|
||||
*/
|
||||
onMaxLength(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 编辑器 focus 时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onFocus(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
this.$emit('focus', editor)
|
||||
},
|
||||
/**
|
||||
* 编辑器 blur 时的回调函数
|
||||
* @param editor
|
||||
*/
|
||||
onBlur(editor) {
|
||||
if (editor == null) {
|
||||
return
|
||||
}
|
||||
this.$emit('blur', editor)
|
||||
},
|
||||
/**
|
||||
* 自定义编辑器 alert 。如想用 ElementUI 的 Message 功能
|
||||
* @param editor
|
||||
*/
|
||||
customAlert(info, type) {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
CsMessage.Error(info)
|
||||
break
|
||||
case 'info':
|
||||
CsMessage.Info(info)
|
||||
break
|
||||
case 'warning':
|
||||
CsMessage.Warning(info)
|
||||
break
|
||||
case 'error':
|
||||
CsMessage.Error(info)
|
||||
break
|
||||
default:
|
||||
CsMessage.Info(info)
|
||||
break
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑
|
||||
* @param editor
|
||||
*/
|
||||
customPaste(editor, event, callback) {
|
||||
if (editor == null) {
|
||||
callback(true)
|
||||
}
|
||||
// console.log('ClipboardEvent 粘贴事件对象', event)
|
||||
// // const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
|
||||
// // const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
|
||||
// // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
|
||||
//
|
||||
// // 自定义插入内容
|
||||
// editor.insertText('xxx')
|
||||
//
|
||||
// // 返回 false ,阻止默认粘贴行为
|
||||
// event.preventDefault()
|
||||
// callback(false) // 返回值(注意,vue 事件的返回值,不能用 return)
|
||||
//
|
||||
// // 返回 true ,继续默认的粘贴行为
|
||||
// // callback(true)
|
||||
callback(true)
|
||||
},
|
||||
/**
|
||||
* 设置编辑器html内容
|
||||
* @param html
|
||||
* <br/>
|
||||
* 如果是 editor.getHtml() 获取的 HTML 格式,可以完美解析。
|
||||
* 如果是其他的 HTML 格式,则不能保证语义正确 —— dangerously 。
|
||||
*/
|
||||
setHtml(html) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.dangerouslyInsertHtml(html)
|
||||
},
|
||||
/**
|
||||
* 获取编辑器html内容
|
||||
*/
|
||||
getHtml() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
this.editor.getHtml()
|
||||
},
|
||||
/**
|
||||
* 清空编辑器内容
|
||||
*/
|
||||
clear() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.clear()
|
||||
},
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
undo() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.undo()
|
||||
},
|
||||
/**
|
||||
* 重做
|
||||
*/
|
||||
redo() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.redo()
|
||||
},
|
||||
/**
|
||||
* 获取选中的文本
|
||||
*/
|
||||
getSelectionText() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getSelectionText()
|
||||
},
|
||||
/**
|
||||
* 删除选中的内容
|
||||
*/
|
||||
deleteFragment() {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.deleteFragment()
|
||||
},
|
||||
/**
|
||||
* 获取选中的内容。JSON格式
|
||||
*/
|
||||
getFragment() {
|
||||
if (this.editor == null) {
|
||||
return {}
|
||||
}
|
||||
return this.editor.getFragment()
|
||||
},
|
||||
/**
|
||||
* 获取文本内容
|
||||
*/
|
||||
getText() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getText()
|
||||
},
|
||||
/**
|
||||
* 在选区插入文本
|
||||
* @param text
|
||||
*/
|
||||
insertText(text) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.insertText(text)
|
||||
},
|
||||
resetField() {
|
||||
this.setHtml('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
* 截屏工具
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<div style="text-align: right">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-camera"
|
||||
:loading="capturing"
|
||||
size="small"
|
||||
@click="capture"
|
||||
>
|
||||
截图并下载
|
||||
</el-button>
|
||||
</div>
|
||||
<div id="elementIdSelector">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
import { FormatDateTimeShortStr } from '@/utils/CsUtil'
|
||||
import html2canvas from 'html2canvas'
|
||||
|
||||
export default {
|
||||
name: 'CuteScreenShot',
|
||||
data() {
|
||||
return {
|
||||
capturing: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async capture() {
|
||||
this.capturing = true
|
||||
const element = document.querySelector('#elementIdSelector')
|
||||
if (!element) {
|
||||
CsMessage.Error('未找到匹配的元素,请检查选择器是否正确')
|
||||
this.capturing = false
|
||||
return
|
||||
}
|
||||
const canvas = await html2canvas(element)
|
||||
// const base64 = canvas.toDataURL('image/png')
|
||||
const link = document.createElement('a')
|
||||
link.download = `screenshot_${FormatDateTimeShortStr(new Date())}.png`
|
||||
link.href = canvas.toDataURL('image/png')
|
||||
link.click()
|
||||
this.capturing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<!--
|
||||
* 查询组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
:inline="true"
|
||||
:model.sync="model"
|
||||
label-position="right"
|
||||
label-width="90px"
|
||||
>
|
||||
<slot name="suffix" />
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onFormSubmit('searchForm')">查询</el-button>
|
||||
<el-button @click="onFormReset('searchForm')">重置</el-button>
|
||||
</el-form-item>
|
||||
<slot name="operation" />
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteSearchForm',
|
||||
components: {},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onFormSubmit(formName) {
|
||||
const that = this
|
||||
that.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
that.$emit('submit', that.model)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
onFormReset(formName) {
|
||||
try {
|
||||
this.$refs[formName].resetFields()
|
||||
this.$emit('reset')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
* 段落标题组件:占一行,支持嵌套按钮
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-13
|
||||
-->
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h4>
|
||||
<i class="el-icon-s-help" style="color: #409EFF;font-size: 20px" />
|
||||
<i style="color: #303133;">{{ title }}</i>
|
||||
</h4>
|
||||
</el-col>
|
||||
<el-col :span="12" style="text-align: right;line-height: 64px;height: 64px;padding-right: 30px">
|
||||
<slot />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteSectionTitle',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,135 +1,149 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
stripe
|
||||
empty-text="暂无数据"
|
||||
:row-key="crud.primaryKey"
|
||||
fit
|
||||
:data="crud.dataSource"
|
||||
style="width: 100%;"
|
||||
:height="height"
|
||||
:max-height="height"
|
||||
@selection-change="onTableSelectionChange"
|
||||
>
|
||||
<el-table-column v-if="mode === 'multi'" type="selection" width="55" />
|
||||
<slot />
|
||||
</el-table>
|
||||
<el-pagination
|
||||
:current-page="pageProps.current"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:page-size="pageProps.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pageProps.total"
|
||||
@size-change="(size) => crud.onPageChange(pageProps.current, size, pageProps.total)"
|
||||
@current-change="(current) => crud.onPageChange(current, pageProps.pageSize, pageProps.total)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteSimpleTable',
|
||||
props: {
|
||||
primaryKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
fetch: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
paramsTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
responseTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'none'
|
||||
},
|
||||
pageProps: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: function() {
|
||||
return {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
crud: {
|
||||
loading: false,
|
||||
primaryKey: this.primaryKey ? this.primaryKey : 'id',
|
||||
fetchUrl: null,
|
||||
dataSource: [],
|
||||
onPageChange: (currentPage, pageSize) => {
|
||||
this.pageProps.current = currentPage
|
||||
this.pageProps.pageSize = pageSize
|
||||
console.log('on-page-change', currentPage, pageSize)
|
||||
// this.$emit('onPageChange', currentPage, pageSize, total)
|
||||
this.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
return document.documentElement.clientHeight - 300
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initData()
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.initData()
|
||||
},
|
||||
async initData() {
|
||||
let params = {}
|
||||
// 自定义请求参数
|
||||
if (this.paramsTransform) {
|
||||
params = this.paramsTransform(this.pageProps)
|
||||
}
|
||||
if (this.fetch) {
|
||||
try {
|
||||
this.crud.loading = true
|
||||
// 请求远程数据
|
||||
const response = await this.fetch(params)
|
||||
// console.error('response', response)
|
||||
// { content: [], totalElements: 0 }
|
||||
// 转换为表格所需要的数据
|
||||
if (this.responseTransform) {
|
||||
return this.responseTransform(response)
|
||||
}
|
||||
if (response && response.hasOwnProperty('totalElements') && response.hasOwnProperty('content')) {
|
||||
this.pageProps.total = response.totalElements
|
||||
this.crud.dataSource = response.content
|
||||
}
|
||||
return response
|
||||
} finally {
|
||||
this.crud.loading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
onTableSelectionChange(selection) {
|
||||
this.$emit('selection-change', selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--
|
||||
* 简单表格组件:封装自定义逻辑与参数
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
stripe
|
||||
empty-text="暂无数据"
|
||||
:row-key="crud.primaryKey"
|
||||
fit
|
||||
:data="crud.dataSource"
|
||||
style="width: 100%;"
|
||||
:height="height"
|
||||
:max-height="height"
|
||||
@selection-change="onTableSelectionChange"
|
||||
>
|
||||
<el-table-column v-if="mode === 'multi'" type="selection" width="55" />
|
||||
<slot />
|
||||
</el-table>
|
||||
<el-pagination
|
||||
:current-page="pageProps.current"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:page-size="pageProps.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pageProps.total"
|
||||
@size-change="(size) => crud.onPageChange(pageProps.current, size, pageProps.total)"
|
||||
@current-change="(current) => crud.onPageChange(current, pageProps.pageSize, pageProps.total)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteSimpleTable',
|
||||
props: {
|
||||
primaryKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
fetch: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
paramsTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
responseTransform: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'none'
|
||||
},
|
||||
pageProps: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: function() {
|
||||
return {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
customHeight: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
crud: {
|
||||
loading: false,
|
||||
primaryKey: this.primaryKey ? this.primaryKey : 'id',
|
||||
fetchUrl: null,
|
||||
dataSource: [],
|
||||
onPageChange: (currentPage, pageSize) => {
|
||||
this.pageProps.current = currentPage
|
||||
this.pageProps.pageSize = pageSize
|
||||
console.log('on-page-change', currentPage, pageSize)
|
||||
// this.$emit('onPageChange', currentPage, pageSize, total)
|
||||
this.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
if (this.customHeight) {
|
||||
return this.customHeight
|
||||
}
|
||||
return document.documentElement.clientHeight - 300
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initData()
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.initData()
|
||||
},
|
||||
async initData() {
|
||||
let params = {}
|
||||
// 自定义请求参数
|
||||
if (this.paramsTransform) {
|
||||
params = this.paramsTransform(this.pageProps)
|
||||
}
|
||||
if (this.fetch) {
|
||||
try {
|
||||
this.crud.loading = true
|
||||
// 请求远程数据
|
||||
const response = await this.fetch(params)
|
||||
// console.error('response', response)
|
||||
// { content: [], totalElements: 0 }
|
||||
// 转换为表格所需要的数据
|
||||
if (this.responseTransform) {
|
||||
return this.responseTransform(response)
|
||||
}
|
||||
if (response && response.hasOwnProperty('totalElements') && response.hasOwnProperty('content')) {
|
||||
this.pageProps.total = response.totalElements
|
||||
this.crud.dataSource = response.content
|
||||
}
|
||||
return response
|
||||
} finally {
|
||||
this.crud.loading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
onTableSelectionChange(selection) {
|
||||
this.$emit('selection-change', selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!--
|
||||
* 纵向滚动条容器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<div class="overflow-panel">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteVerticalScrollBar'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.overflow-panel {
|
||||
height: 800px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/* 滚动条宽度 */
|
||||
.overflow-panel::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
.overflow-panel::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
.overflow-panel::-webkit-scrollbar-thumb {
|
||||
background: #409EFF;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 鼠标悬停在滑块上时 */
|
||||
.overflow-panel::-webkit-scrollbar-thumb:hover {
|
||||
background: #F56C6C;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,92 +1,94 @@
|
||||
<!--Yaml编辑器 基于-->
|
||||
<!--<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>,-->
|
||||
<!--主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>-->
|
||||
<!--</p>-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
|
||||
import 'codemirror/theme/darcula.css'
|
||||
import 'codemirror/mode/yaml/yaml'
|
||||
|
||||
export default {
|
||||
name: 'CuteYamlEditor',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '500px'
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
content(newVal, oldVal) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (newVal !== editorValue) {
|
||||
this.editor.setValue(newVal)
|
||||
}
|
||||
},
|
||||
height(newVal, oldVal) {
|
||||
this.editor.setSize('auto', newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: 'text/x-yaml',
|
||||
autoRefresh: true,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 1,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'darcula'
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
if (this.content) {
|
||||
this.editor.setValue(this.content)
|
||||
}
|
||||
this.editor.setOption('readOnly', this.readOnly)
|
||||
this.editor.on('change', cm => {
|
||||
this.$emit('change', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setValue(val) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.setValue(val)
|
||||
},
|
||||
resetField() {
|
||||
this.editor.setValue('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
* YAML编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeMirror from 'codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
// 替换主题这里需修改名称
|
||||
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
|
||||
import 'codemirror/theme/darcula.css'
|
||||
import 'codemirror/mode/yaml/yaml'
|
||||
|
||||
export default {
|
||||
name: 'CuteYamlEditor',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '500px'
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
content(newVal, oldVal) {
|
||||
const editorValue = this.editor.getValue()
|
||||
if (newVal !== editorValue) {
|
||||
this.editor.setValue(newVal)
|
||||
}
|
||||
},
|
||||
height(newVal, oldVal) {
|
||||
this.editor.setSize('auto', newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
|
||||
mode: 'text/x-yaml',
|
||||
autoRefresh: true,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
lineWrapping: true,
|
||||
tabSize: 2,
|
||||
cursorHeight: 1,
|
||||
// 替换主题这里需修改名称
|
||||
theme: 'darcula'
|
||||
})
|
||||
this.editor.setSize('auto', this.height)
|
||||
if (this.content) {
|
||||
this.editor.setValue(this.content)
|
||||
}
|
||||
this.editor.setOption('readOnly', this.readOnly)
|
||||
this.editor.on('change', cm => {
|
||||
this.$emit('change', cm.getValue())
|
||||
this.$emit('input', cm.getValue())
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValue() {
|
||||
if (this.editor == null) {
|
||||
return ''
|
||||
}
|
||||
return this.editor.getValue()
|
||||
},
|
||||
setValue(val) {
|
||||
if (this.editor == null) {
|
||||
return
|
||||
}
|
||||
this.editor.setValue(val)
|
||||
},
|
||||
resetField() {
|
||||
this.editor.setValue('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user