This commit is contained in:
sakuya 2021-05-24 23:00:24 +08:00
parent 195d7d570a
commit a68c3201e9
15 changed files with 348 additions and 42 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "SCUI-Admin", "name": "SCUI-Admin",
"version": "1.0.9", "version": "1.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",

View File

@ -58,6 +58,15 @@
"type": "menu" "type": "menu"
}, },
"component": "vab/upload" "component": "vab/upload"
},
{
"path": "/vab/editor",
"name": "editor",
"meta": {
"title": "富文本编辑器",
"type": "menu"
},
"component": "vab/editor"
} }
] ]
}, },

File diff suppressed because one or more lines are too long

7
src/components/scEditor/ckeditor.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,109 @@
<template>
<div class="sceditor">
<div class="toolbar-container"></div>
<div class="content-container">
<ckeditor v-model="value" :editor="editor" :config="editorConfig" :disabled="disabled" @ready="onReady"
@input="onEditorInput"></ckeditor>
</div>
</div>
</template>
<script>
import ClassicEditor from './build-classic/ckeditor.js';
import CKEditor from './ckeditor.js';
export default {
components: {
ckeditor: CKEditor.component
},
props: {
modelValue: {
type: String,
default: ""
},
placeholder: {
type: String,
default: ""
},
disabled: {
type: Boolean,
default: false
},
toolbar: {
type: Array,
default: () => { return ['heading', '|', 'fontSize', 'fontFamily', 'fontColor', '|', 'bold', 'italic', 'underline', 'strikethrough', '|', 'alignment', '|', 'bulletedList', 'numberedList', '|', 'outdent', 'indent', '|', 'todoList', 'blockQuote', 'link', 'imageUpload', 'mediaEmbed', 'insertTable', '|', 'undo', 'redo'] }
}
},
data() {
return {
value: this.modelValue,
editor: ClassicEditor,
editorConfig: {
toolbar: this.toolbar,
placeholder: this.placeholder,
fontSize: {
options: ['default', 10, 12, 14, 16, 18, 20, 24, 36]
},
image: {
styles: ['alignLeft', 'alignCenter', 'alignRight'],
toolbar: [ 'imageStyle:alignCenter', 'imageStyle:alignLeft', 'imageStyle:alignRight', '|', 'imageTextAlternative' ]
},
table: {
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', 'tableCellProperties', 'tableProperties']
},
ckfinder: {
uploadUrl: 'https://www.fastmock.site/mock/44c807475f7eeba73409792255781935/api/upload'
}
}
}
},
watch: {
modelValue() {
this.value = this.modelValue
},
},
mounted() {
},
methods: {
onReady(editor) {
const toolbarContainer = document.querySelector('.toolbar-container');
toolbarContainer.prepend(editor.ui.view.toolbar.element);
},
onEditorInput() {
this.$emit('update:modelValue', this.value);
}
}
}
</script>
<style>
.sceditor {}
.ck .ck-placeholder:before {color: #bbb;}
.sceditor .ck-toolbar {
background: #fff;
border-color: #DCDFE6;
box-shadow: 2px 2px 1px rgba(0, 0, 0, .05);
position: relative;
z-index: 1;
}
.content-container {
background: #f6f8f9;
overflow-y: scroll;
padding: 30px;
height: 400px;
border: 1px solid #DCDFE6;
border-top: 0;
resize: vertical;
}
.content-container .ck-content {
margin: 0 auto;
background: #fff;
border: 1px solid #DCDFE6 !important;
box-shadow: 2px 2px 1px rgba(0, 0, 0, .05) !important;
padding: 40px;
min-height: 340px;
}
</style>

View File

@ -3,7 +3,7 @@ const DEFAULT_CONFIG = {
APP_NAME: "SCUI", APP_NAME: "SCUI",
//版本号 //版本号
APP_VER: "1.0.9", APP_VER: "1.1.0",
//接口地址 //接口地址
API_URL: "", API_URL: "",

View File

@ -14,6 +14,7 @@ import permission from './utils/permission'
import scTable from './components/scTable' import scTable from './components/scTable'
import scFilterBar from './components/scFilterBar' import scFilterBar from './components/scFilterBar'
import scUpload from './components/scUpload' import scUpload from './components/scUpload'
import scUploadMultiple from './components/scUpload/multiple'
const app = createApp(App); const app = createApp(App);
@ -30,5 +31,6 @@ app.use(ElementPlus, {size: 'small', zIndex: 1000 ,locale: locale});
app.component('scTable', scTable); app.component('scTable', scTable);
app.component('scFilterBar', scFilterBar); app.component('scFilterBar', scFilterBar);
app.component('scUpload', scUpload); app.component('scUpload', scUpload);
app.component('scUploadMultiple', scUploadMultiple);
app.mount('#app'); app.mount('#app');

View File

@ -22,6 +22,13 @@
data() { data() {
return { return {
activities: [ activities: [
{
content: [
"[优化] 重构上传组件",
"[新增] 富文本编辑器",
],
timestamp: '2021-05-24'
},
{ {
content: [ content: [
"[优化] 控制台自由布局开关", "[优化] 控制台自由布局开关",

View File

@ -43,7 +43,7 @@
</template> </template>
</el-dialog> </el-dialog>
<el-dialog title="角色权限设置" v-model="permissionDialogVisible" :width="400" destroy-on-close> <el-dialog title="角色权限设置" v-model="permissionDialogVisible" :width="500" destroy-on-close>
<permission-dialog ref="permissionDialog"></permission-dialog> <permission-dialog ref="permissionDialog"></permission-dialog>
<template #footer> <template #footer>
<el-button @click="permissionDialogVisible=false" > </el-button> <el-button @click="permissionDialogVisible=false" > </el-button>

View File

@ -1,5 +1,5 @@
<template> <template>
<el-tabs tab-position="left" style="height: 280px;"> <el-tabs tab-position="top">
<el-tab-pane label="菜单权限"> <el-tab-pane label="菜单权限">
<div class="treeMain"> <div class="treeMain">
<el-tree ref="menu" node-key="name" :data="menu.list" :default-checked-keys="menu.checked" :props="menu.props" show-checkbox></el-tree> <el-tree ref="menu" node-key="name" :data="menu.list" :default-checked-keys="menu.checked" :props="menu.props" show-checkbox></el-tree>

View File

@ -1,29 +0,0 @@
<template>
<el-main>
<el-card shadow="never">
<el-tabs tab-position="top">
<el-tab-pane label="系统设置">
</el-tab-pane>
<el-tab-pane label="业务配置">业务配置</el-tab-pane>
<el-tab-pane label="扩展配置">扩展配置</el-tab-pane>
</el-tabs>
</el-card>
</el-main>
</template>
<script>
export default {
name: 'system',
data() {
return {
sys: {
name: ""
}
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,161 @@
<template>
<el-main>
<el-card shadow="never">
<el-tabs tab-position="top">
<el-tab-pane label="系统设置">
<el-form ref="form" :model="sys" label-width="100px" style="margin-top: 20px;">
<el-form-item label="系统名称">
<el-input v-model="sys.name"></el-input>
</el-form-item>
<el-form-item label="LogoUrl">
<el-input v-model="sys.logoUrl"></el-input>
</el-form-item>
<el-form-item label="登录开关">
<el-switch v-model="sys.login"></el-switch>
<div class="el-form-item-msg" data-v-b33b3cf8="">关闭后普通用户无法登录仅允许管理员角色登录</div>
</el-form-item>
<el-form-item label="密码验证规则">
<el-input v-model="sys.passwordRules"></el-input>
</el-form-item>
<el-form-item label="版权信息">
<el-input type="textarea" :autosize="{minRows: 4}" v-model="sys.copyright"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="短信配置">
<el-form ref="form" :model="msg" label-width="100px" style="margin-top: 20px;">
<el-form-item label="短信开关">
<el-switch v-model="msg.open"></el-switch>
<div class="el-form-item-msg" data-v-b33b3cf8="">关闭后用户无法收到短信但日志中将记录</div>
</el-form-item>
<el-form-item label="appKey">
<el-input v-model="msg.appKey"></el-input>
</el-form-item>
<el-form-item label="secretKey">
<el-input v-model="msg.secretKey"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="扩展配置">
<el-alert title="扩展配置为系统业务所有的配置,应该由系统管理员操作,如需用户配置应另起业务配置页面。" type="warning"></el-alert>
<el-table :data="setting" stripe>
<el-table-column label="#" type="index" width="50"></el-table-column>
<el-table-column label="KEY" prop="key" width="150">
<template #default="scope">
<el-input v-if="scope.row.isSet" v-model="scope.row.key" placeholder="请输入内容"></el-input>
<span v-else>{{scope.row.key}}</span>
</template>
</el-table-column>
<el-table-column label="VALUE" prop="value" width="350">
<template #default="scope">
<template v-if="scope.row.isSet">
<el-switch v-if="typeof scope.row.value==='boolean'" v-model="scope.row.value"></el-switch>
<el-input v-else v-model="scope.row.value" placeholder="请输入内容"></el-input>
</template>
<span v-else>{{scope.row.value}}</span>
</template>
</el-table-column>
<el-table-column label="TITLE" prop="title" width="350">
<template #default="scope">
<el-input v-if="scope.row.isSet" v-model="scope.row.title" placeholder="请输入内容"></el-input>
<span v-else>{{scope.row.title}}</span>
</template>
</el-table-column>
<el-table-column min-width="1"></el-table-column>
<el-table-column label="操作" fixed="right" width="100">
<template #default="scope">
<el-button @click="table_edit(scope.row, scope.$index)" type="text" size="small">{{scope.row.isSet?'保存':"修改"}}</el-button>
<el-button v-if="scope.row.isSet" @click="scope.row.isSet=false" type="text" size="small">取消</el-button>
<el-popconfirm v-if="!scope.row.isSet" title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-button type="primary" icon="el-icon-plus" @click="table_add" style="margin-top: 20px;"></el-button>
</el-tab-pane>
</el-tabs>
</el-card>
</el-main>
</template>
<script>
export default {
name: 'system',
data() {
return {
sys: {
name: "SCUI",
logoUrl: "",
login: true,
passwordRules: "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$",
copyright: "@SCUI"
},
msg: {
open: true,
appKey: "",
secretKey: ""
},
setting: [
{
key: "file_serve",
value: "https://file.scui.com",
title: "文件服务器地址"
},
{
key: "cloud_url",
value: "-",
title: "客户端地址"
},
{
key: "crm_url",
value: "-",
title: "CRM地址"
},
{
key: "autoSwitch",
value: true,
title: "自动判断boolean类型"
}
]
}
},
methods: {
table_add(){
var newRow = {
key: "",
value: "",
title: "",
isSet: true
}
this.setting.push(newRow)
},
table_edit(row){
if(row.isSet){
row.isSet = false
}else{
row.isSet = true
}
},
table_del(row, index){
this.setting.splice(index, 1)
},
}
}
</script>
<style>
</style>

27
src/views/vab/editor.vue Normal file
View File

@ -0,0 +1,27 @@
<template>
<el-main>
<el-card shadow="never">
<sc-editor v-model="html" placeholder="请输入"></sc-editor>
</el-card>
</el-main>
</template>
<script>
import { defineAsyncComponent } from 'vue';
const scEditor = defineAsyncComponent(() => import('@/components/scEditor'));
export default {
name: "editor",
components: {
scEditor
},
data(){
return {
html: '<h2 style="text-align:center;"><span style="font-size:24px;">CKEditor 5</span></h2><p style="text-align:center;">SCUI 二次封装</p><p>&nbsp;</p><p>&nbsp;</p><p>关于富文本编辑器真的是个大坑,在选择的过程中我也走了不少弯路,基本上世面上几款主流的富文本编辑器都试过来了。</p><p>&nbsp;</p><p>先说下为什么选择ckeditor5作为SCUI的富文本默认编辑器第一次用ckeditor是早期DEDECMS年代它后台所使用的就是ckeditor经过了近10年的更新也迭代到了5.0而ckeditor官网的online builder(在线编译)是我最后选择它的原因。不用import各种样式、工具和插件傻瓜式的操作生成出中文的自定义组件的属于自己的ckeditor5它的文档也是相比的几款编辑器中最全演示最多的唯一不足的就是没有官方中文文档。最后它的几款增值服务真的非常牛(协同编辑等),有兴趣的同学可以去它的 <a href="https://ckeditor.com/"><span style="color:hsl(210, 75%, 60%);">官网</span></a> 了解下。</p><p>&nbsp;</p><p>这次封装后支持v-model双向绑定模式也开放了几个props</p><figure class="table" style="width:100%;"><table><tbody><tr><td style="background-color:hsl(0, 0%, 60%);padding:15px;width:200px;" colspan="3"><p style="text-align:center;"><span style="color:hsl(0, 0%, 100%);"><strong>props</strong></span></p></td></tr><tr><td style="background-color:hsl(0, 0%, 90%);padding:15px;width:200px;">v-model</td><td style="padding:15px;width:200px;">String</td><td style="padding:15px;">编辑器内的富文本</td></tr><tr><td style="background-color:hsl(0, 0%, 90%);padding:15px;">placeholder</td><td style="padding:15px;">String</td><td style="padding:15px;">属性提供可描述输入字段预期值的提示信息,默认为空</td></tr><tr><td style="background-color:hsl(0, 0%, 90%);padding:15px;">toolbar</td><td style="padding:15px;">Array</td><td style="padding:15px;">需要显示的头部工具,配置相当暴力直接,默认全部</td></tr><tr><td style="background-color:hsl(0, 0%, 90%);padding:15px;">disabled</td><td style="padding:15px;">Boolean</td><td style="padding:15px;">是否禁用编辑器</td></tr></tbody></table></figure><p>当前页面使用了异步加载组件因为ckeditor5打包后的文件实在太大了1M采用异步加载可极大加快页面打开速度。正因为文件大小的关系不再考虑将编辑器作为全局组件毕竟使用频次比表单之类的要少很多。因为非常独立耦合度基本为零可随意删除减少打包后的文件大小</p>'
}
}
}
</script>
<style>
</style>

View File

@ -11,6 +11,10 @@
<p class="no-print">忽略打印</p> <p class="no-print">忽略打印</p>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="动态打印">
<el-alert title="打印创建的DOM结构,适用于远程获取模板后打印" type="success" style="margin-bottom:20px;"></el-alert>
<el-button type="primary" @click="print2">动态打印</el-button>
</el-tab-pane>
</el-tabs> </el-tabs>
</el-card> </el-card>
</el-main> </el-main>
@ -26,7 +30,14 @@
}, },
methods: { methods: {
print(){ print(){
//REFquerySelector
print(this.$refs.printMain) print(this.$refs.printMain)
},
print2(){
//DOM
var dom = document.createElement("div")
dom.innerHTML = "<div>后创建的DOM结构</div>"
print(dom)
} }
} }
} }

View File

@ -3,8 +3,8 @@
<el-card shadow="never" header="基础示例"> <el-card shadow="never" header="基础示例">
<sc-upload v-model="imgurl" :action="uploadUrl"></sc-upload> <sc-upload v-model="imgurl" :action="uploadUrl"></sc-upload>
<sc-upload v-model="avatar" title="自定义标题" icon="el-icon-picture-outline" :action="uploadUrl"></sc-upload> <sc-upload v-model="imgurl2" title="自定义标题" icon="el-icon-picture-outline" :action="uploadUrl"></sc-upload>
<sc-upload v-model="avatar" :action="uploadUrl" accept=".xls,.xlsx" :on-success="success" :width="220"> <sc-upload v-model="imgurl3" :action="uploadUrl" accept=".xls,.xlsx" :on-success="success" :width="220">
<div class="custom-empty"> <div class="custom-empty">
<i class="el-icon-upload"></i> <i class="el-icon-upload"></i>
<p>自定义插槽</p> <p>自定义插槽</p>
@ -43,19 +43,14 @@
</template> </template>
<script> <script>
import multiple from '@/components/scUpload/multiple'
export default { export default {
name: 'upload', name: 'upload',
components: {
scUploadMultiple: multiple
},
data() { data() {
return { return {
uploadUrl: this.$API.demo.upload.url, uploadUrl: this.$API.demo.upload.url,
imgurl: "images/avatar.jpg", imgurl: "images/avatar.jpg",
avatar: "", imgurl2: "",
imgs: "images/avatar.jpg,images/avatar2.gif,images/avatar3.gif", imgurl3: "",
form: { form: {
img1: "", img1: "",
img2: "", img2: "",