Commit ad024d07 authored by liuyang's avatar liuyang

init project

parents
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
"plugins": [
"@babel/plugin-syntax-dynamic-import"
],
sourceType: 'unambiguous'
}
{
"name": "wx-manage",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"@tinymce/tinymce-vue": "^3.0.1",
"axios": "^0.19.0",
"cos-js-sdk-v5": "^0.5.20",
"vue": "^2.6.10",
"vue-cookie": "^1.1.4",
"vue-router": "^3.1.3",
"vuex": "^3.0.1"
},
"devDependencies": {
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@vue/cli-plugin-babel": "^4.0.0",
"@vue/cli-service": "^4.0.0",
"node-sass": "^4.12.0",
"sass-loader": "^8.0.0",
"vue-template-compiler": "^2.6.10"
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link href="https://unpkg.com/element-ui@2.12.0/lib/theme-chalk/index.css" rel="stylesheet">
<title>微信CMS后台管理系统</title>
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<script src="https://unpkg.com/element-ui@2.12.0/lib/index.js"></script>
<script src="https://cdn.bootcss.com/tinymce/5.0.16/tinymce.min.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but weixin-manage doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
/* html相关样式 */
a {
color: #4285f4;
}
h1,h2,h3,h4,h5,h6{
margin: 0.3rem 0;
color: #0064A8;
line-height: 2rem;
}
h1{
font-size: 1.4rem;
}
h2{
font-size: 1.2rem;
}
h3{
font-size: 1.1rem;
}
h4,
h5,
h6 {
font-size: 1rem;
}
hr {
height: 0.2em;
border: 0;
color: #CCCCCC;
background-color: #CCCCCC;
}
p,
blockquote,
ul,
ol,
dl,
li,
table,
pre {
margin: 8px 0;
}
p {
margin: 1em 0;
line-height: 1.5rem;
}
pre {
background-color: #F8F8F8;
border: 1px solid #CCCCCC;
border-radius: 3px;
overflow: auto;
padding: 5px;
}
blockquote {
color: #666666;
margin: 0;
border-left: 0.2em #EEE solid;
}
ul,
ol {
margin: 1em 0;
padding: 0 0 0 2em;
}
li p:last-child {
margin: 0
}
dd {
margin: 0 0 0 2em;
}
img {
border: 0;
max-width: 300px;
display: block;
object-fit: contain;
width: auto !important;
height: auto !important;
}
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
border: 1px solid #eee;
}
td {
vertical-align: top;
padding: 0.2em 0;
border-top: 1px solid #EEEEEE;
}
\ No newline at end of file
tinymce.addI18n('zh_CN',{
"Redo": "\u91cd\u505a",
"Undo": "\u64a4\u9500",
"Cut": "\u526a\u5207",
"Copy": "\u590d\u5236",
"Paste": "\u7c98\u8d34",
"Select all": "\u5168\u9009",
"New document": "\u65b0\u6587\u4ef6",
"Ok": "\u786e\u5b9a",
"Cancel": "\u53d6\u6d88",
"Visual aids": "\u7f51\u683c\u7ebf",
"Bold": "\u7c97\u4f53",
"Italic": "\u659c\u4f53",
"Underline": "\u4e0b\u5212\u7ebf",
"Strikethrough": "\u5220\u9664\u7ebf",
"Superscript": "\u4e0a\u6807",
"Subscript": "\u4e0b\u6807",
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
"Justify": "\u4e24\u7aef\u5bf9\u9f50",
"Bullet list": "\u9879\u76ee\u7b26\u53f7",
"Numbered list": "\u7f16\u53f7\u5217\u8868",
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
"Close": "\u5173\u95ed",
"Formats": "\u683c\u5f0f",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
"Headers": "\u6807\u9898",
"Header 1": "\u6807\u98981",
"Header 2": "\u6807\u98982",
"Header 3": "\u6807\u98983",
"Header 4": "\u6807\u98984",
"Header 5": "\u6807\u98985",
"Header 6": "\u6807\u98986",
"Headings": "\u6807\u9898",
"Heading 1": "\u6807\u98981",
"Heading 2": "\u6807\u98982",
"Heading 3": "\u6807\u98983",
"Heading 4": "\u6807\u98984",
"Heading 5": "\u6807\u98985",
"Heading 6": "\u6807\u98986",
"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
"Div": "Div",
"Pre": "Pre",
"Code": "\u4ee3\u7801",
"Paragraph": "\u6bb5\u843d",
"Blockquote": "\u5f15\u6587\u533a\u5757",
"Inline": "\u6587\u672c",
"Blocks": "\u57fa\u5757",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
"Fonts": "\u5b57\u4f53",
"Font Sizes": "\u5b57\u53f7",
"Class": "\u7c7b\u578b",
"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
"OR": "\u6216",
"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
"Upload": "\u4e0a\u4f20",
"Block": "\u5757",
"Align": "\u5bf9\u9f50",
"Default": "\u9ed8\u8ba4",
"Circle": "\u7a7a\u5fc3\u5706",
"Disc": "\u5b9e\u5fc3\u5706",
"Square": "\u65b9\u5757",
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Anchor...": "\u951a\u70b9...",
"Name": "\u540d\u79f0",
"Id": "\u6807\u8bc6\u7b26",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
"Special characters...": "\u7279\u6b8a\u5b57\u7b26...",
"Source code": "\u6e90\u4ee3\u7801",
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
"Language": "\u8bed\u8a00",
"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
"Color Picker": "\u9009\u8272\u5668",
"R": "R",
"G": "G",
"B": "B",
"Left to right": "\u4ece\u5de6\u5230\u53f3",
"Right to left": "\u4ece\u53f3\u5230\u5de6",
"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
"Title": "\u6807\u9898",
"Keywords": "\u5173\u952e\u8bcd",
"Description": "\u63cf\u8ff0",
"Robots": "\u673a\u5668\u4eba",
"Author": "\u4f5c\u8005",
"Encoding": "\u7f16\u7801",
"Fullscreen": "\u5168\u5c4f",
"Action": "\u64cd\u4f5c",
"Shortcut": "\u5feb\u6377\u952e",
"Help": "\u5e2e\u52a9",
"Address": "\u5730\u5740",
"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
"Plugins": "\u63d2\u4ef6",
"Handy Shortcuts": "\u5feb\u6377\u952e",
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
"Image description": "\u56fe\u7247\u63cf\u8ff0",
"Source": "\u5730\u5740",
"Dimensions": "\u5927\u5c0f",
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
"General": "\u666e\u901a",
"Advanced": "\u9ad8\u7ea7",
"Style": "\u6837\u5f0f",
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
"Border": "\u8fb9\u6846",
"Insert image": "\u63d2\u5165\u56fe\u7247",
"Image...": "\u56fe\u7247...",
"Image list": "\u56fe\u7247\u5217\u8868",
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
"Edit image": "\u7f16\u8f91\u56fe\u7247",
"Image options": "\u56fe\u7247\u9009\u9879",
"Zoom in": "\u653e\u5927",
"Zoom out": "\u7f29\u5c0f",
"Crop": "\u88c1\u526a",
"Resize": "\u8c03\u6574\u5927\u5c0f",
"Orientation": "\u65b9\u5411",
"Brightness": "\u4eae\u5ea6",
"Sharpen": "\u9510\u5316",
"Contrast": "\u5bf9\u6bd4\u5ea6",
"Color levels": "\u989c\u8272\u5c42\u6b21",
"Gamma": "\u4f3d\u9a6c\u503c",
"Invert": "\u53cd\u8f6c",
"Apply": "\u5e94\u7528",
"Back": "\u540e\u9000",
"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Text to display": "\u663e\u793a\u6587\u5b57",
"Url": "\u5730\u5740",
"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
"Current window": "\u5f53\u524d\u7a97\u53e3",
"None": "\u65e0",
"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
"Remove link": "\u5220\u9664\u94fe\u63a5",
"Anchors": "\u951a\u70b9",
"Link...": "\u94fe\u63a5...",
"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
"Link list": "\u94fe\u63a5\u5217\u8868",
"Insert video": "\u63d2\u5165\u89c6\u9891",
"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
"Alternative source": "\u955c\u50cf",
"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
"Embed": "\u5185\u5d4c",
"Media...": "\u591a\u5a92\u4f53...",
"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
"Page break": "\u5206\u9875\u7b26",
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
"Preview": "\u9884\u89c8",
"Print...": "\u6253\u5370...",
"Save": "\u4fdd\u5b58",
"Find": "\u67e5\u627e",
"Replace with": "\u66ff\u6362\u4e3a",
"Replace": "\u66ff\u6362",
"Replace all": "\u5168\u90e8\u66ff\u6362",
"Previous": "\u4e0a\u4e00\u4e2a",
"Next": "\u4e0b\u4e00\u4e2a",
"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
"Find whole words only": "\u5168\u5b57\u5339\u914d",
"Spell check": "\u62fc\u5199\u68c0\u67e5",
"Ignore": "\u5ffd\u7565",
"Ignore all": "\u5168\u90e8\u5ffd\u7565",
"Finish": "\u5b8c\u6210",
"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
"Insert table": "\u63d2\u5165\u8868\u683c",
"Table properties": "\u8868\u683c\u5c5e\u6027",
"Delete table": "\u5220\u9664\u8868\u683c",
"Cell": "\u5355\u5143\u683c",
"Row": "\u884c",
"Column": "\u5217",
"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
"Delete row": "\u5220\u9664\u884c",
"Row properties": "\u884c\u5c5e\u6027",
"Cut row": "\u526a\u5207\u884c",
"Copy row": "\u590d\u5236\u884c",
"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
"Delete column": "\u5220\u9664\u5217",
"Cols": "\u5217",
"Rows": "\u884c",
"Width": "\u5bbd",
"Height": "\u9ad8",
"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
"Show caption": "\u663e\u793a\u6807\u9898",
"Left": "\u5de6\u5bf9\u9f50",
"Center": "\u5c45\u4e2d",
"Right": "\u53f3\u5bf9\u9f50",
"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
"Scope": "\u8303\u56f4",
"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
"H Align": "\u6c34\u5e73\u5bf9\u9f50",
"V Align": "\u5782\u76f4\u5bf9\u9f50",
"Top": "\u9876\u90e8\u5bf9\u9f50",
"Middle": "\u5782\u76f4\u5c45\u4e2d",
"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
"Header cell": "\u8868\u5934\u5355\u5143\u683c",
"Row group": "\u884c\u7ec4",
"Column group": "\u5217\u7ec4",
"Row type": "\u884c\u7c7b\u578b",
"Header": "\u8868\u5934",
"Body": "\u8868\u4f53",
"Footer": "\u8868\u5c3e",
"Border color": "\u8fb9\u6846\u989c\u8272",
"Insert template...": "\u63d2\u5165\u6a21\u677f...",
"Templates": "\u6a21\u677f",
"Template": "\u6a21\u677f",
"Text color": "\u6587\u5b57\u989c\u8272",
"Background color": "\u80cc\u666f\u8272",
"Custom...": "\u81ea\u5b9a\u4e49...",
"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
"No color": "\u65e0",
"Remove color": "\u79fb\u9664\u989c\u8272",
"Table of Contents": "\u5185\u5bb9\u5217\u8868",
"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
"Word count": "\u5b57\u6570",
"Words: {0}": "\u5b57\u6570\uff1a{0}",
"{0} words": "{0} \u5b57",
"File": "\u6587\u4ef6",
"Edit": "\u7f16\u8f91",
"Insert": "\u63d2\u5165",
"View": "\u89c6\u56fe",
"Format": "\u683c\u5f0f",
"Table": "\u8868\u683c",
"Tools": "\u5de5\u5177",
"Powered by {0}": "\u7531{0}\u9a71\u52a8",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
"Image title": "\u56fe\u7247\u6807\u9898",
"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
"Border style": "\u8fb9\u6846\u6837\u5f0f",
"Error": "\u9519\u8bef",
"Warn": "\u8b66\u544a",
"Valid": "\u6709\u6548",
"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
"System Font": "\u7cfb\u7edf\u5b57\u4f53",
"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
"example": "\u793a\u4f8b",
"Search": "\u641c\u7d22",
"All": "\u5168\u90e8",
"Currency": "\u8d27\u5e01",
"Text": "\u6587\u5b57",
"Quotations": "\u5f15\u7528",
"Mathematical": "\u6570\u5b66",
"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
"Symbols": "\u7b26\u53f7",
"Arrows": "\u7bad\u5934",
"User Defined": "\u81ea\u5b9a\u4e49",
"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
"currency sign": "\u8d27\u5e01\u7b26\u53f7",
"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
"colon sign": "\u5192\u53f7",
"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
"lira sign": "\u91cc\u62c9\u7b26\u53f7",
"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
"naira sign": "\u5948\u62c9\u7b26\u53f7",
"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
"won sign": "\u97e9\u5143\u7b26\u53f7",
"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
"austral sign": "\u6fb3\u5143\u7b26\u53f7",
"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
"cedi sign": "\u585e\u5730\u7b26\u53f7",
"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
"spesmilo sign": "spesmilo\u7b26\u53f7",
"tenge sign": "\u575a\u6208\u7b26\u53f7",
"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
"ruble sign": "\u5362\u5e03\u7b26\u53f7",
"yen character": "\u65e5\u5143\u5b57\u6837",
"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
"People": "\u4eba\u7c7b",
"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
"Activity": "\u6d3b\u52a8",
"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
"Objects": "\u7269\u4ef6",
"Flags": "\u65d7\u5e1c",
"Characters": "\u5b57\u7b26",
"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
"Update": "\u66f4\u65b0",
"Color swatch": "\u989c\u8272\u6837\u672c",
"Turquoise": "\u9752\u7eff\u8272",
"Green": "\u7eff\u8272",
"Blue": "\u84dd\u8272",
"Purple": "\u7d2b\u8272",
"Navy Blue": "\u6d77\u519b\u84dd",
"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
"Dark Green": "\u6df1\u7eff\u8272",
"Medium Blue": "\u4e2d\u84dd\u8272",
"Medium Purple": "\u4e2d\u7d2b\u8272",
"Midnight Blue": "\u6df1\u84dd\u8272",
"Yellow": "\u9ec4\u8272",
"Orange": "\u6a59\u8272",
"Red": "\u7ea2\u8272",
"Light Gray": "\u6d45\u7070\u8272",
"Gray": "\u7070\u8272",
"Dark Yellow": "\u6697\u9ec4\u8272",
"Dark Orange": "\u6df1\u6a59\u8272",
"Dark Red": "\u6df1\u7ea2\u8272",
"Medium Gray": "\u4e2d\u7070\u8272",
"Dark Gray": "\u6df1\u7070\u8272",
"Black": "\u9ed1\u8272",
"White": "\u767d\u8272",
"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
"history": "\u5386\u53f2",
"styles": "\u6837\u5f0f",
"formatting": "\u683c\u5f0f\u5316",
"alignment": "\u5bf9\u9f50",
"indentation": "\u7f29\u8fdb",
"permanent pen": "\u8bb0\u53f7\u7b14",
"comments": "\u5907\u6ce8",
"Anchor": "\u951a\u70b9",
"Special character": "\u7279\u6b8a\u7b26\u53f7",
"Code sample": "\u4ee3\u7801\u793a\u4f8b",
"Color": "\u989c\u8272",
"Emoticons": "\u8868\u60c5",
"Document properties": "\u6587\u6863\u5c5e\u6027",
"Image": "\u56fe\u7247",
"Insert link": "\u63d2\u5165\u94fe\u63a5",
"Target": "\u6253\u5f00\u65b9\u5f0f",
"Link": "\u94fe\u63a5",
"Poster": "\u5c01\u9762",
"Media": "\u5a92\u4f53",
"Print": "\u6253\u5370",
"Prev": "\u4e0a\u4e00\u4e2a",
"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
"Whole words": "\u5168\u5b57\u5339\u914d",
"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
"Caption": "\u6807\u9898",
"Insert template": "\u63d2\u5165\u6a21\u677f"
});
\ No newline at end of file
<template>
<div id="app">
<transition name="fade">
<router-view />
</transition>
</div>
</template>
<style>
img.image-sm {
max-width: 80px;
max-height: 80px;
}
.el-col .el-select,.el-col .el-date-editor{
width: 100%;
}
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
.text-warning{
color: #E6A23C;
}
</style>
@charset "utf-8";
*{
box-sizing: border-box;
}
#app-menu ul{
padding:0;
}
#app-menu li{
list-style:none;
}
#app-menu{
overflow: hidden;
width:100%;
}
.weixin-preview{
position: relative;
width: 320px;
height: 540px;
float: left;
margin-right:10px;
border: 1px solid #e7e7eb;
}
.weixin-preview a{
text-decoration: none;
color: #616161;
}
.weixin-preview .weixin-hd .weixin-title{
color:#fff;
font-size:15px;
width:100%;
text-align: center;
position:absolute;
top: 33px;
left: 0px;
}
.weixin-preview .weixin-bd{
}
.weixin-preview .weixin-menu{
position: absolute;
bottom: 0;
left: 0;
right: 0;
border-top: 1px solid #e7e7e7;
background-position: 0 0;
background-repeat: no-repeat;
margin-bottom:0px;
}
/*一级*/
.weixin-preview .weixin-menu .menu-item{
position: relative;
float: left;
line-height: 50px;
height:50px;
text-align: center;
width:33.33%;
border-left: 1px solid #e7e7e7;
cursor: pointer;
color:#616161;
}
/*二级*/
.weixin-preview .weixin-sub-menu{
position: absolute;
bottom: 60px;
left: 0;
right: 0;
border-top: 1px solid #d0d0d0;
margin-bottom:0px;
background: #fafafa;
display: block;
padding:0;
}
.weixin-preview .weixin-sub-menu .menu-sub-item{
line-height: 50px;
height:50px;
text-align: center;
width:100%;
border: 1px solid #d0d0d0;
border-top-width: 0px;
cursor: pointer;
position: relative;
color:#616161;
}
.weixin-preview .menu-arrow{
position: absolute;
left: 50%;
margin-left: -6px;
}
.weixin-preview .arrow_in{
bottom: -4px;
display: inline-block;
width: 0px;
height: 0px;
border-width: 6px 6px 0px;
border-style: solid dashed dashed;
border-color: #fafafa transparent transparent;
}
.weixin-preview .arrow_out{
bottom: -5px;
display: inline-block;
width: 0px;
height: 0px;
border-width: 6px 6px 0px;
border-style: solid dashed dashed;
border-color: #d0d0d0 transparent transparent;
}
.weixin-preview .menu-item .menu-item-title,.weixin-preview .menu-sub-item .menu-item-title{
width:100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
box-sizing: border-box;
}
.weixin-preview .menu-item.current,.weixin-preview .menu-sub-item.current{
border: 1px solid #44b549;
background: #fff;
color:#44b549;
}
.weixin-preview .weixin-sub-menu.show{
display:block;
}
.weixin-preview .icon_menu_dot{
/* background: url(../images/index_z354723.png) 0px -36px no-repeat; */
width: 7px;
height: 7px;
vertical-align: middle;
display: inline-block;
margin-right: 2px;
margin-top: -2px;
}
.weixin-preview .icon14_menu_add{
/* background: url(../images/index_z354723.png) 0px 0px no-repeat; */
width: 14px;
height: 14px;
vertical-align: middle;
display: inline-block;
margin-top: -2px;
}
.weixin-preview li:hover .icon14_menu_add{
/* background: url(../images/index_z354723.png) 0px -18px no-repeat; */
}
.weixin-preview .menu-item:hover{
color: #000;
}
.weixin-preview .menu-sub-item:hover{
background: #eee;
}
.weixin-preview li.current:hover{
background: #fff;
color: #44b549;
}
/*菜单内容*/
.weixin-menu-detail{
width: 600px;
padding: 0px 20px 5px;
background-color: #f4f5f9;
border: 1px solid #e7e7eb;
float: left;
min-height: 540px;
}
.weixin-menu-detail .menu-name{
float: left;
height: 40px;
line-height: 40px;
font-size: 18px;
}
.weixin-menu-detail .menu-del{
float: right;
height: 40px;
line-height: 40px;
color: #459ae9;
cursor:pointer;
}
.weixin-menu-detail .menu-input-group{
width:540px;
margin:10px 0 30px 0;
overflow: hidden;
}
.weixin-menu-detail .menu-label{
float: left;
height: 30px;
line-height: 30px;
width:80px;
text-align: right;
}
.weixin-menu-detail .menu-input{
float:left;
width: 380px
}
.weixin-menu-detail .menu-input-text{
border: 0px;
outline: 0px;
background: #fff;
width: 300px;
padding: 5px 0px 5px 0px;
margin-left: 10px;
text-indent: 10px;
height: 35px;
}
.weixin-menu-detail .menu-tips{
color: #8d8d8d;
padding-top: 4px;
margin:0;
}
.weixin-menu-detail .menu-tips.cursor{
color: #459ae9;
cursor: pointer;
}
.weixin-menu-detail .menu-input .menu-tips{
margin:0 0 0 10px;
}
.weixin-menu-detail .menu-content{
padding: 16px 20px;
border: 1px solid #e7e7eb;
background-color: #fff;
}
.weixin-menu-detail .menu-content .menu-input-group{
margin: 0px 0 10px 0;
}
.weixin-menu-detail .menu-content .menu-label{
text-align: left;
width:100px;
}
.weixin-menu-detail .menu-content .menu-input-text{
border: 1px solid #e7e7eb;
}
.weixin-menu-detail .menu-content .menu-tips{
padding-bottom: 10px;
}
.weixin-menu-detail .menu-msg-content{
padding: 0;
border: 1px solid #e7e7eb;
background-color: #fff;
}
.weixin-menu-detail .menu-msg-content .menu-msg-head{
overflow: hidden;
border-bottom: 1px solid #e7e7eb;
line-height: 38px;
height: 38px;
padding: 0 20px;
}
.weixin-menu-detail .menu-msg-content .menu-msg-panel{
padding: 30px 50px;
}
.weixin-menu-detail .menu-msg-content .menu-msg-select{
padding: 40px 20px;
border: 2px dotted #d9dadc;
text-align: center;
}
.weixin-menu-detail .menu-msg-content .menu-msg-select:hover{
border-color: #b3b3b3;
}
.weixin-menu-detail .menu-msg-content strong{
display: block;
padding-top: 3px;
font-weight: 400;
font-style: normal;
}
.weixin-menu-detail .menu-msg-content .menu-msg-title{
float: left;
width: 310px;
height: 30px;
line-height: 30px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon36_common{
width: 36px;
height: 36px;
vertical-align: middle;
display: inline-block;
}
.icon_msg_sender{
margin-right: 3px;
margin-top: -2px;
width: 20px;
height: 20px;
vertical-align: middle;
display: inline-block;
/* background: url(../images/msg_tab_z25df2d.png) 0 -270px no-repeat; */
}
.weixin-btn-group{
text-align: center;
width:100%;
margin:30px 0px;
overflow: hidden;
}
.weixin-btn-group .btn{
width: 100px;
border-radius: 0px;
}
#material-list{
padding:20px;
overflow-y:scroll;
height: 558px;
}
#news-list{
padding:20px;
overflow-y:scroll;
height: 558px;
}
#material-list table{
width:100%;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px;
line-height: 1.15;
color: #303133;
background-color: #fff;
}
a {
color: mix(#fff, $--color-primary, 20%);
text-decoration: none;
&:focus,
&:hover {
color: $--color-primary;
text-decoration: underline;
}
}
img {
vertical-align: middle;
}
/* Utils
------------------------------ */
.clearfix:before,
.clearfix:after {
content: " ";
display: table;
}
.clearfix:after {
clear: both;
}
/* Animation
------------------------------ */
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
/* Reset element-ui
------------------------------ */
.site-wrapper {
.el-pagination {
margin-top: 15px;
text-align: right;
}
}
/* Layout
------------------------------ */
.site-wrapper {
position: relative;
min-width: 1180px;
}
/* Sidebar fold
------------------------------ */
.site-sidebar--fold {
.site-navbar__header,
.site-navbar__brand,
.site-sidebar,
.site-sidebar__inner,
.el-menu.site-sidebar__menu {
width: 64px;
}
.site-navbar__body,
.site-content__wrapper {
margin-left: 64px;
}
.site-navbar__brand {
&-lg {
display: none;
}
&-mini {
display: inline-block;
}
}
.site-sidebar,
.site-sidebar__inner {
overflow: initial;
}
.site-sidebar__menu-icon {
margin-right: 0;
font-size: 20px;
}
.site-content--tabs > .el-tabs > .el-tabs__header {
left: 64px;
}
}
// animation
.site-navbar__header,
.site-navbar__brand,
.site-navbar__body,
.site-sidebar,
.site-sidebar__inner,
.site-sidebar__menu.el-menu,
.site-sidebar__menu-icon,
.site-content__wrapper,
.site-content--tabs > .el-tabs .el-tabs__header {
transition: inline-block .3s, left .3s, width .3s, margin-left .3s, font-size .3s;
}
/* Navbar
------------------------------ */
.site-navbar {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 1030;
height: 50px;
box-shadow: 0 2px 4px rgba(0, 0, 0, .08);
background-color: $navbar--background-color;
&--inverse {
.site-navbar__body {
background-color: transparent;
}
.el-menu {
> .el-menu-item,
> .el-submenu > .el-submenu__title {
color: #fff;
&:focus,
&:hover {
color: #fff;
background-color: mix(#000, $navbar--background-color, 15%);
}
}
> .el-menu-item.is-active,
> .el-submenu.is-active > .el-submenu__title {
border-bottom-color: mix(#fff, $navbar--background-color, 85%);
}
.el-menu-item i,
.el-submenu__title i,
.el-dropdown {
color: #fff;
}
}
.el-menu--popup-bottom-start {
background-color: $navbar--background-color;
}
}
&__header {
position: relative;
float: left;
width: 230px;
height: 50px;
overflow: hidden;
}
&__brand {
display: table-cell;
vertical-align: middle;
width: 230px;
height: 50px;
margin: 0;
line-height: 50px;
font-size: 20px;
text-align: center;
text-transform: uppercase;
white-space: nowrap;
color: #fff;
&-lg,
&-mini {
margin: 0 5px;
color: #fff;
&:focus,
&:hover {
color: #fff;
text-decoration: none;
}
}
&-mini {
display: none;
}
}
&__switch {
font-size: 18px;
border-bottom: none !important;
}
&__avatar {
border-bottom: none !important;
* {
vertical-align: inherit;
}
.el-dropdown-link {
> img {
width: 36px;
height: auto;
margin-right: 5px;
border-radius: 100%;
vertical-align: middle;
}
}
}
&__body {
position: relative;
margin-left: 230px;
padding-right: 15px;
background-color: #fff;
}
&__menu {
float: left;
background-color: transparent;
border-bottom: 0;
&--right {
float: right;
}
a:focus,
a:hover {
text-decoration: none;
}
.el-menu-item,
.el-submenu > .el-submenu__title {
height: 50px;
line-height: 50px;
}
.el-submenu > .el-menu {
top: 55px;
}
.el-badge {
display: inline;
z-index: 2;
&__content {
line-height: 16px;
}
}
}
}
/* Sidebar
------------------------------ */
.site-sidebar {
position: fixed;
top: 50px;
left: 0;
bottom: 0;
z-index: 1020;
width: 230px;
overflow: hidden;
&--dark,
&--dark-popper {
background-color: $sidebar--background-color-dark;
.site-sidebar__menu.el-menu,
> .el-menu--popup {
background-color: $sidebar--background-color-dark;
.el-menu-item,
.el-submenu > .el-submenu__title {
color: $sidebar--color-text-dark;
&:focus,
&:hover {
color: mix(#fff, $sidebar--color-text-dark, 50%);
background-color: mix(#fff, $sidebar--background-color-dark, 2.5%);
}
}
.el-menu,
.el-submenu.is-opened {
background-color: mix(#000, $sidebar--background-color-dark, 15%);
}
.el-menu-item.is-active,
.el-submenu.is-active > .el-submenu__title {
color: mix(#fff, $sidebar--color-text-dark, 80%);
}
}
}
&__inner {
position: relative;
z-index: 1;
width: 250px;
height: 100%;
padding-bottom: 15px;
overflow-y: scroll;
}
&__menu.el-menu {
width: 230px;
border-right: 0;
}
&__menu-icon {
width: 24px;
margin-right: 5px;
text-align: center;
font-size: 16px;
color: inherit !important;
}
}
/* Content
------------------------------ */
.site-content {
position: relative;
padding: 15px;
&__wrapper {
position: relative;
padding-top: 50px;
margin-left: 230px;
min-height: 100%;
background: $content--background-color;
}
&--tabs {
padding: 55px 0 0;
}
> .el-tabs {
> .el-tabs__header {
position: fixed;
top: 50px;
left: 230px;
right: 0;
z-index: 930;
padding: 0 55px 0 15px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .12), 0 0 6px 0 rgba(0, 0, 0, .04);
background-color: #fff;
> .el-tabs__nav-wrap {
margin-bottom: 0;
&:after {
display: none;
}
}
}
> .el-tabs__content {
padding: 0 15px 15px;
> .site-tabs__tools {
position: fixed;
top: 50px;
right: 0;
z-index: 931;
height: 40px;
padding: 0 12px;
font-size: 16px;
line-height: 40px;
background-color: $content--background-color;
cursor: pointer;
.el-icon--right {
margin-left: 0;
}
}
}
}
}
.el-table__expand-icon {
display: inline-block;
width: 14px;
vertical-align: middle;
margin-right: 5px;
}
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/
html {
line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
*/
body {
margin: 0;
}
/**
* Add the correct display in IE 9-.
*/
article,
aside,
footer,
header,
nav,
section {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* Add the correct display in IE 9-.
* 1. Add the correct display in IE.
*/
figcaption,
figure,
main { /* 1 */
display: block;
}
/**
* Add the correct margin in IE 8.
*/
figure {
margin: 1em 40px;
}
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* 1. Remove the gray background on active links in IE 10.
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/
a {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
}
/**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/
b,
strong {
font-weight: inherit;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font style in Android 4.3-.
*/
dfn {
font-style: italic;
}
/**
* Add the correct background and color in IE 9-.
*/
mark {
background-color: #ff0;
color: #000;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
audio,
video {
display: inline-block;
}
/**
* Add the correct display in iOS 4-7.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Remove the border on images inside links in IE 10-.
*/
img {
border-style: none;
}
/**
* Hide the overflow in IE.
*/
svg:not(:root) {
overflow: hidden;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers (opinionated).
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: sans-serif; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
button,
html [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; /* 2 */
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* 1. Add the correct display in IE 9-.
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Remove the default vertical scrollbar in IE.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in IE 9-.
* 1. Add the correct display in Edge, IE, and Firefox.
*/
details, /* 1 */
menu {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Scripting
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
canvas {
display: inline-block;
}
/**
* Add the correct display in IE.
*/
template {
display: none;
}
/* Hidden
========================================================================== */
/**
* Add the correct display in IE 10-.
*/
[hidden] {
display: none;
}
\ No newline at end of file
// 站点主色
// tips: 要达到整站主题修改效果, 请确保[$--color-primary]站点主色与[/src/element-ui-theme/index.js]文件中[import './element-[#17B3A3]/index.css']当前主题色一致
$--color-primary: #409EFF;
// Navbar
$navbar--background-color: $--color-primary;
// Sidebar
$sidebar--background-color-dark: #263238;
$sidebar--color-text-dark: #8a979e;
// Content
$content--background-color: #f1f4f5;
@import "normalize"; // api: https://github.com/necolas/normalize.css/
@import "variables"; // 站点变量
@import "base";
<template>
<svg
:class="getClassName"
:width="width"
:height="height"
aria-hidden="true">
<use :xlink:href="getName"></use>
</svg>
</template>
<script>
export default {
name: 'icon-svg',
props: {
name: {
type: String,
required: true
},
className: {
type: String
},
width: {
type: String
},
height: {
type: String
}
},
computed: {
getName () {
return `#icon-${this.name}`
},
getClassName () {
return [
'icon-svg',
`icon-svg__${this.name}`,
this.className && /\S/.test(this.className) ? `${this.className}` : ''
]
}
}
}
</script>
<style>
.icon-svg {
width: 1em;
height: 1em;
fill: currentColor;
overflow: hidden;
}
</style>
<template>
<el-table-column :prop="prop" v-bind="$attrs">
<template slot-scope="scope">
<span @click.prevent="toggleHandle(scope.$index, scope.row)" :style="childStyles(scope.row)">
<i :class="iconClasses(scope.row)" :style="iconStyles(scope.row)"></i>
{{ scope.row[prop] }}
</span>
</template>
</el-table-column>
</template>
<script>
import isArray from 'lodash/isArray'
export default {
name: 'table-tree-column',
props: {
prop: {
type: String
},
treeKey: {
type: String,
default: 'id'
},
parentKey: {
type: String,
default: 'parentId'
},
levelKey: {
type: String,
default: '_level'
},
childKey: {
type: String,
default: 'children'
}
},
methods: {
childStyles (row) {
return { 'padding-left': (row[this.levelKey] > 1 ? row[this.levelKey] * 7 : 0) + 'px' }
},
iconClasses (row) {
return [ !row._expanded ? 'el-icon-caret-right' : 'el-icon-caret-bottom' ]
},
iconStyles (row) {
return { 'visibility': this.hasChild(row) ? 'visible' : 'hidden' }
},
hasChild (row) {
return (isArray(row[this.childKey]) && row[this.childKey].length >= 1) || false
},
// 切换处理
toggleHandle (index, row) {
if (this.hasChild(row)) {
var data = this.$parent.store.states.data.slice(0)
data[index]._expanded = !data[index]._expanded
if (data[index]._expanded) {
data = data.splice(0, index + 1).concat(row[this.childKey]).concat(data)
} else {
data = this.removeChildNode(data, row[this.treeKey])
}
this.$parent.store.commit('setData', data)
this.$nextTick(() => {
this.$parent.doLayout()
})
}
},
// 移除子节点
removeChildNode (data, parentId) {
var parentIds = isArray(parentId) ? parentId : [parentId]
if (parentId.length <= 0) {
return data
}
var ids = []
for (var i = 0; i < data.length; i++) {
if (parentIds.indexOf(data[i][this.parentKey]) !== -1 && parentIds.indexOf(data[i][this.treeKey]) === -1) {
data[i]._expanded = false
ids.push(data.splice(i, 1)[0][this.treeKey])
i--
}
}
return this.removeChildNode(data, ids)
}
}
}
</script>
<template>
<div class="panel flex flex-wrap">
<el-tag
v-for="tag in dynamicTags"
closable
@close="handleClose(tag)"
:disable-transitions="false"
:key="tag">
{{tag}}
</el-tag>
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
size="small"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm">
</el-input>
<el-button v-else class="button-new-tag" size="small" @click="showInput">+ 添加</el-button>
</div>
</template>
<script>
/**
* 标签编辑器
*/
let touchMoved=false;
export default {
name:'tags-editor',
props:{
value:{
type:String,
required:true,
default:""
},
size:{//标签大小:[small:小,large:大]
type:String,
default:'small'
}
},
data(){
return {
inputVisible: false,
inputValue: ''
}
},
computed:{
dynamicTags(){
if(this.value!="") return this.value.split(',')
return []
}
},
methods:{
handleClose(tag) {
let newTags = this.dynamicTags;
newTags.splice(newTags.indexOf(tag), 1);
this.$emit('input',newTags.join(","));
},
showInput() {
this.inputVisible = true;
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus();
});
},
handleInputConfirm() {
let inputValue = this.inputValue;
let newTags = this.dynamicTags;
if (inputValue && newTags.indexOf(inputValue)<0) {
newTags.push(inputValue);
}
this.inputVisible = false;
this.inputValue = '';
this.$emit('input',newTags.join(","));
}
}
}
</script>
<style scoped>
.panel{
flex: 1;
}
.el-tag + .el-tag {
margin-left: 10px;
}
.input-new-tag{
width: inherit;
}
.button-new-tag{
margin-left: 10px;
}
</style>
<template>
<div class="tinymce-editor">
<editor v-model="myValue" :init="init" @onExecCommand="onExecCommand"></editor>
</div>
</template>
<script>
// import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
// import "tinymce/themes/silver";
var COS = require("cos-js-sdk-v5"); //腾讯云对象存储
var cos;
export default {
name: "tinymce-editor",
components: {
Editor
},
props: {
value: {
type: String,
default: ""
}
},
data() {
return {
init: {
language_url: "./tinymce/zh_CN.js", //public目录下
language: "zh_CN",
height: 500,
plugins: "lists image media table paste link searchreplace anchor code preview pagebreak importcss",
toolbar: "undo redo searchreplace | formatselect pagebreak | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists link anchor image media table | removeformat code preview", //工具栏展示项
toolbar_drawer: false,
image_advtab: true,
object_resizing : false,
paste_data_images: true,
content_css: "./tinymce/article.css",
images_upload_handler: (blobInfo, success, failure) => {
this.uploadFile(blobInfo.blob()).then(fileUrl=>success(fileUrl)).catch(err=>failure(err))
}
},
myValue: this.value,
uploading: false,
cosConfig: []
};
},
mounted() {
// console.log('tinymce-editor mounted:',this.value)
tinymce.init({});
this.cosInit();
},
methods: {
cosInit() {
this.$http({
url: this.$http.adornUrl("/sys/oss/config"),
method: "get",
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
this.cosConfig = data.config;
cos = new COS({
SecretId: data.config.qcloudSecretId,
SecretKey: data.config.qcloudSecretKey
});
} else {
this.$message.error("请先配置云存储相关信息!");
}
});
},
onExecCommand(e) {
//console.log(e)
},
uploadFile(file) {
this.uploading = true;
let now = new Date();
let path = now.toISOString().slice(0, 10) + "/" + now.getTime() + file.name.substr(file.name.lastIndexOf("."));
return new Promise((resolve, reject) => {
cos.putObject({
Bucket: this.cosConfig.qcloudBucketName /* 必须 */,
Region: this.cosConfig.qcloudRegion /* 必须 */,
Key: path /* 必须 */,
Body: file // 上传文件对象
},(err, data) => {
console.log(err || data);
this.uploading = false;
if (data) {
let fileUrl ="https://" +this.cosConfig.qcloudBucketName +".cos." +this.cosConfig.qcloudRegion +".myqcloud.com/" +path;
resolve(fileUrl);
this.saveUploadResult(fileUrl);
} else {
this.$message.error("文件上传失败", err);
reject(err);
}
});
});
},
saveUploadResult(url) {
this.$http({
url: this.$http.adornUrl("/sys/oss/upload"),
method: "post",
data: {
url: url
}
}).then(({ data }) => {
this.$emit("uploaded", url);
});
}
},
watch: {
value(newValue) {
this.myValue = newValue;
},
myValue(newValue) {
this.$emit("input", newValue);
}
}
};
</script>
\ No newline at end of file
/**
* UI组件, 统一使用饿了么桌面端组件库(https://github.com/ElemeFE/element)
*
* 使用:
* 1. 项目中需要的组件进行释放(解开注释)
*
* 注意:
* 1. 打包只会包含释放(解开注释)的组件, 减少打包文件大小
*/
import Vue from 'vue'
import {
Pagination,
Dialog,
Autocomplete,
Dropdown,
DropdownMenu,
DropdownItem,
Menu,
Submenu,
MenuItem,
MenuItemGroup,
Input,
InputNumber,
Radio,
RadioGroup,
RadioButton,
Checkbox,
CheckboxButton,
CheckboxGroup,
Switch,
Select,
Option,
OptionGroup,
Button,
ButtonGroup,
Table,
TableColumn,
DatePicker,
TimeSelect,
TimePicker,
Popover,
Tooltip,
Breadcrumb,
BreadcrumbItem,
Form,
FormItem,
Tabs,
TabPane,
Tag,
Tree,
Alert,
Slider,
Icon,
Row,
Col,
Upload,
Progress,
Badge,
Card,
Rate,
Steps,
Step,
Carousel,
CarouselItem,
Collapse,
CollapseItem,
Cascader,
ColorPicker,
Transfer,
Container,
Header,
Aside,
Main,
Footer,
Loading,
MessageBox,
Message,
Notification
} from 'element-ui'
Vue.use(Pagination)
Vue.use(Dialog)
Vue.use(Autocomplete)
Vue.use(Dropdown)
Vue.use(DropdownMenu)
Vue.use(DropdownItem)
Vue.use(Menu)
Vue.use(Submenu)
Vue.use(MenuItem)
Vue.use(MenuItemGroup)
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Radio)
Vue.use(RadioGroup)
Vue.use(RadioButton)
Vue.use(Checkbox)
Vue.use(CheckboxButton)
Vue.use(CheckboxGroup)
Vue.use(Switch)
Vue.use(Select)
Vue.use(Option)
Vue.use(OptionGroup)
Vue.use(Button)
Vue.use(ButtonGroup)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(DatePicker)
Vue.use(TimeSelect)
Vue.use(TimePicker)
Vue.use(Popover)
Vue.use(Tooltip)
Vue.use(Breadcrumb)
Vue.use(BreadcrumbItem)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Tabs)
Vue.use(TabPane)
Vue.use(Tag)
Vue.use(Tree)
Vue.use(Alert)
Vue.use(Slider)
Vue.use(Icon)
Vue.use(Row)
Vue.use(Col)
Vue.use(Upload)
Vue.use(Progress)
Vue.use(Badge)
Vue.use(Card)
Vue.use(Rate)
Vue.use(Steps)
Vue.use(Step)
Vue.use(Carousel)
Vue.use(CarouselItem)
Vue.use(Collapse)
Vue.use(CollapseItem)
Vue.use(Cascader)
Vue.use(ColorPicker)
Vue.use(Transfer)
Vue.use(Container)
Vue.use(Header)
Vue.use(Aside)
Vue.use(Main)
Vue.use(Footer)
Vue.use(Loading.directive)
Vue.prototype.$loading = Loading.service
Vue.prototype.$msgbox = MessageBox
Vue.prototype.$alert = MessageBox.alert
Vue.prototype.$confirm = MessageBox.confirm
Vue.prototype.$prompt = MessageBox.prompt
Vue.prototype.$notify = Notification
Vue.prototype.$message = Message
Vue.prototype.$ELEMENT = { size: 'medium' }
// import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import VueCookie from 'vue-cookie'
// import './element-ui'
// import './element-ui-theme'
import './assets/scss/index.scss'
import httpRequest from '@/utils/httpRequest' // api: https://github.com/axios/axios
import { isAuth } from '@/utils'
Vue.use(VueCookie)
Vue.config.productionTip = false
// 挂载全局
Vue.prototype.$http = httpRequest // ajax请求方法
Vue.prototype.isAuth = isAuth // 权限方法
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
module.exports = file => () => import('@/views/' + file + '.vue')
/**
* 全站路由配置
*
* 建议:
* 1. 代码中路由统一使用name属性跳转(不使用path属性)
*/
// import Vue from 'vue'
// import Router from 'vue-router'
import http from '@/utils/httpRequest'
import { isURL } from '@/utils/validate'
import { clearLoginInfo } from '@/utils'
// Vue.use(Router)
const _import = require('./import-views')
// 全局路由(无需嵌套上左右整体布局)
const globalRoutes = [
{ path: '/404', component: ()=>import('@/views/common/404'), name: '404', meta: { title: '404未找到' } },
{ path: '/login', component: ()=>import('@/views/common/login'), name: 'login', meta: { title: '登录' } }
]
// 主入口路由(需嵌套上左右整体布局)
const mainRoutes = {
path: '/',
component: ()=>import('@/views/main'),
name: 'main',
redirect: { name: 'home' },
meta: { title: '主入口整体布局' },
children: [
// 通过meta对象设置路由展示方式
// 1. isTab: 是否通过tab展示内容, true: 是, false: 否
// 2. iframeUrl: 是否通过iframe嵌套展示内容, '以http[s]://开头': 是, '': 否
// 提示: 如需要通过iframe嵌套展示内容, 但不通过tab打开, 请自行创建组件使用iframe处理!
{ path: '/home', component: ()=>import('@/views/common/home'), name: 'home', meta: { title: '首页' } },
{ path: '/theme', component: ()=>import('@/views/common/theme'), name: 'theme', meta: { title: '主题' } },
{ path: '/article-add-or-update', component: ()=>import('@/views/modules/wx/article-add-or-update'), name: 'article', meta: { title: '编辑文章', isTab: true } },
{ path: '/material-news-add-or-update', component: ()=>import('@/views/modules/wx/material-news-add-or-update'), name: 'material-news', meta: { title: '图文素材' ,isDynamic:true,isTab:true} },
],
beforeEnter (to, from, next) {
let token = Vue.cookie.get('token')
if (!token || !/\S/.test(token)) {
clearLoginInfo()
next({ name: 'login' })
}
next()
}
}
const router = new VueRouter({
mode: 'hash',
scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes)
})
router.beforeEach((to, from, next) => {
// 添加动态(菜单)路由
// 1. 已经添加 or 全局路由, 直接访问
// 2. 获取菜单列表, 添加并保存本地存储
if (router.options.isAddDynamicMenuRoutes || fnCurrentRouteType(to, globalRoutes) === 'global') {
next()
} else {
http({
url: http.adornUrl('/sys/menu/nav'),
method: 'get',
params: http.adornParams()
}).then(({data}) => {
if (data && data.code === 200) {
fnAddDynamicMenuRoutes(data.menuList)
router.options.isAddDynamicMenuRoutes = true
sessionStorage.setItem('menuList', JSON.stringify(data.menuList || '[]'))
sessionStorage.setItem('permissions', JSON.stringify(data.permissions || '[]'))
next({ ...to, replace: true })
} else {
sessionStorage.setItem('menuList', '[]')
sessionStorage.setItem('permissions', '[]')
next()
}
}).catch((e) => {
console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue')
router.push({ name: 'login' })
})
}
})
/**
* 判断当前路由类型, global: 全局路由, main: 主入口路由
* @param {*} route 当前路由
*/
function fnCurrentRouteType (route, globalRoutes = []) {
var temp = []
for (var i = 0; i < globalRoutes.length; i++) {
if (route.path === globalRoutes[i].path) {
return 'global'
} else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) {
temp = temp.concat(globalRoutes[i].children)
}
}
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main'
}
/**
* 添加动态(菜单)路由
* @param {*} menuList 菜单列表
* @param {*} routes 递归创建的动态(菜单)路由
*/
function fnAddDynamicMenuRoutes (menuList = [], routes = []) {
var temp = []
for (var i = 0; i < menuList.length; i++) {
if (menuList[i].list && menuList[i].list.length >= 1) {
temp = temp.concat(menuList[i].list)
} else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, '')
var route = {
path: menuList[i].url.replace('/', '-'),
component: null,
name: menuList[i].url.replace('/', '-'),
meta: {
menuId: menuList[i].menuId,
title: menuList[i].name,
isDynamic: true,
isTab: true,
iframeUrl: ''
}
}
// url以http[s]://开头, 通过iframe展示
if (isURL(menuList[i].url)) {
route['path'] = `i-${menuList[i].menuId}`
route['name'] = `i-${menuList[i].menuId}`
route['meta']['iframeUrl'] = menuList[i].url
} else {
try {
route['component'] = _import(`modules/${menuList[i].url}`) || null
// route['component'] = ()=>import(`@/views/modules/${menuList[i].url}.vue`) || null
} catch (e) {}
}
routes.push(route)
}
}
if (temp.length >= 1) {
fnAddDynamicMenuRoutes(temp, routes)
} else {
mainRoutes.name = 'main-dynamic'
mainRoutes.children = routes
router.addRoutes([
mainRoutes,
{ path: '*', redirect: { name: '404' } }
])
sessionStorage.setItem('dynamicMenuRoutes', JSON.stringify(mainRoutes.children || '[]'))
console.log('\n')
console.log('%c!<-------------------- 动态(菜单)路由 s -------------------->', 'color:blue')
console.log(mainRoutes.children)
console.log('%c!<-------------------- 动态(菜单)路由 e -------------------->', 'color:blue')
}
}
export default router
import Vue from 'vue'
import Vuex from 'vuex'
import common from './modules/common'
import user from './modules/user'
import article from './modules/article'
import message from './modules/message'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
common,
user,
article,
message
},
mutations: {
},
strict: process.env.NODE_ENV !== 'production'
})
export default {
namespaced: true,
state: {
ARTICLE_TYPES: {
1:'普通文章',
2:'弹出公告',
3:'滚动公告',
4:'图片公告',
5:'帮助中心',
},
ARTICLE_CATEGORIES: [
"公告"
]
},
mutations: {
}
}
\ No newline at end of file
import router from '@/router'
export default {
namespaced: true,
state: {
// 页面文档可视高度(随窗口改变大小)
documentClientHeight: 0,
// 导航条, 布局风格, defalut(默认) / inverse(反向)
navbarLayoutType: 'default',
// 侧边栏, 布局皮肤, light(浅色) / dark(黑色)
sidebarLayoutSkin: 'dark',
// 侧边栏, 折叠状态
sidebarFold: false,
// 侧边栏, 菜单
menuList: [],
menuActiveName: '',
// 内容, 是否需要刷新
contentIsNeedRefresh: false,
// 主入口标签页
mainTabs: [],
mainTabsActiveName: ''
},
mutations: {
updateDocumentClientHeight(state, height) {
state.documentClientHeight = height
},
updateNavbarLayoutType(state, type) {
state.navbarLayoutType = type
},
updateSidebarLayoutSkin(state, skin) {
state.sidebarLayoutSkin = skin
},
updateSidebarFold(state, fold) {
state.sidebarFold = fold
},
updateMenuList(state, list) {
state.menuList = list
},
updateMenuActiveName(state, name) {
state.menuActiveName = name
},
updateContentIsNeedRefresh(state, status) {
state.contentIsNeedRefresh = status
},
updateMainTabs(state, tabs) {
state.mainTabs = tabs
},
updateMainTabsActiveName(state, name) {
state.mainTabsActiveName = name
},
removeTab(state,tabName) {
state.mainTabs=state.mainTabs.filter(item => item.name !== tabName)
if (state.mainTabs.length >= 1) {
// 当前选中tab被删除
if (tabName === state.mainTabsActiveName) {
var tab = state.mainTabs[state.mainTabs.length - 1]
router.push({ name: tab.name, query: tab.query, params: tab.params }, () => {
state.mainTabsActiveName = tab.name
})
}
} else {
state.menuActiveName = ''
router.push({ name: 'home' })
}
},
closeCurrentTab(state){
this.commit('common/removeTab',state.mainTabsActiveName)
}
}
}
\ No newline at end of file
export default {
namespaced: true,
state: {
KefuMsgType:{
"text":"文本消息",
"image":"图片消息",
"voice":"语音消息",
"video":"视频消息",
"music":"音乐消息",
"news":"外链图文消息",
"mpnews":"公众号图文消息",
"wxcard":"卡券消息",
"miniprogrampage":"小程序消息",
"msgmenu":"菜单消息"
}
},
mutations: {
}
}
\ No newline at end of file
export default {
namespaced: true,
state: {
id: 0,
name: ''
},
mutations: {
updateId (state, id) {
state.id = id
},
updateName (state, name) {
state.name = name
}
}
}
import Vue from 'vue'
import axios from 'axios'
import router from '@/router'
import qs from 'qs'
import merge from 'lodash/merge'
import { clearLoginInfo } from '@/utils'
const baseUrl='/wx'
const http = axios.create({
timeout: 1000 * 30,
withCredentials: true,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
})
/**
* 请求拦截
*/
http.interceptors.request.use(config => {
config.headers['token'] = Vue.cookie.get('token') // 请求头带上token
return config
}, error => {
return Promise.reject(error)
})
/**
* 响应拦截
*/
http.interceptors.response.use(response => {
if (response.data && response.data.code === 401) { // 401, token失效
clearLoginInfo()
router.push({ name: 'login' })
}
return response
}, error => {
return Promise.reject(error)
})
/**
* 请求地址处理
* @param {*} actionName action方法名称
*/
http.adornUrl = (actionName) => {
// 非生产环境 && 开启代理, 接口前缀统一使用[/proxyApi/]前缀做代理拦截!
return baseUrl + actionName
}
/**
* get请求参数处理
* @param {*} params 参数对象
* @param {*} openDefultParams 是否开启默认参数?
*/
http.adornParams = (params = {}, openDefultParams = true) => {
var defaults = {
't': new Date().getTime()
}
return openDefultParams ? merge(defaults, params) : params
}
/**
* post请求数据处理
* @param {*} data 数据对象
* @param {*} openDefultdata 是否开启默认数据?
* @param {*} contentType 数据格式
* json: 'application/json; charset=utf-8'
* form: 'application/x-www-form-urlencoded; charset=utf-8'
*/
http.adornData = (data = {}, openDefultdata = true, contentType = 'json') => {
var defaults = {
't': new Date().getTime()
}
data = openDefultdata ? merge(defaults, data) : data
return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data)
}
export default http
import Vue from 'vue'
import router from '@/router'
import store from '@/store'
/**
* 获取uuid
*/
export function getUUID () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
})
}
/**
* 是否有权限
* @param {*} key
*/
export function isAuth (key) {
return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
}
/**
* 树形数据转换
* @param {*} data
* @param {*} id
* @param {*} pid
*/
export function treeDataTranslate (data, id = 'id', pid = 'parentId') {
var res = []
var temp = {}
for (var i = 0; i < data.length; i++) {
temp[data[i][id]] = data[i]
}
for (var k = 0; k < data.length; k++) {
if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) {
if (!temp[data[k][pid]]['children']) {
temp[data[k][pid]]['children'] = []
}
if (!temp[data[k][pid]]['_level']) {
temp[data[k][pid]]['_level'] = 1
}
data[k]['_level'] = temp[data[k][pid]]._level + 1
temp[data[k][pid]]['children'].push(data[k])
} else {
res.push(data[k])
}
}
return res
}
/**
* 清除登录信息
*/
export function clearLoginInfo () {
Vue.cookie.delete('token')
//store.commit('resetStore')
router.options.isAddDynamicMenuRoutes = false
}
/**
* 邮箱
* @param {*} s
*/
export function isEmail (s) {
return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s)
}
/**
* 手机号码
* @param {*} s
*/
export function isMobile (s) {
return /^1[0-9]{10}$/.test(s)
}
/**
* 电话号码
* @param {*} s
*/
export function isPhone (s) {
return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s)
}
/**
* URL地址
* @param {*} s
*/
export function isURL (s) {
return /^http[s]?:\/\/.*/.test(s)
}
<template>
<div class="site-wrapper site-page--not-found">
<div class="site-content__wrapper">
<div class="site-content">
<h2 class="not-found-title">400</h2>
<p class="not-found-desc">抱歉!您访问的页面<em>失联</em>啦 ...</p>
<el-button @click="$router.go(-1)">返回上一页</el-button>
<el-button type="primary" class="not-found-btn-gohome" @click="$router.push({ name: 'home' })">进入首页</el-button>
</div>
</div>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss">
.site-wrapper.site-page--not-found {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
.site-content__wrapper {
padding: 0;
margin: 0;
background-color: #fff;
}
.site-content {
position: fixed;
top: 15%;
left: 50%;
z-index: 2;
padding: 30px;
text-align: center;
transform: translate(-50%, 0);
}
.not-found-title {
margin: 20px 0 15px;
font-size: 10em;
font-weight: 400;
color: rgb(55, 71, 79);
}
.not-found-desc {
margin: 0 0 30px;
font-size: 26px;
text-transform: uppercase;
color: rgb(118, 131, 143);
> em {
font-style: normal;
color: #ee8145;
}
}
.not-found-btn-gohome {
margin-left: 30px;
}
}
</style>
<template>
<div class="mod-home">
<h3>欢迎使用微信CMS管理系统</h3>
</div>
</template>
<script>
export default {
data() {
return {
countData: {},
visitInfo: [],
};
},
mounted() {
this.getAccessToken()
},
methods: {
getAccessToken(){
this.$http({
url: this.$http.adornUrl('/manage/wxAccessToken/getAccessToken'),
method: 'get'
})
}
}
};
</script>
<style>
.mod-home {
line-height: 2.5;
text-align: center;
}
</style>
<template>
<div class="site-wrapper site-page--login">
<div class="site-content__wrapper">
<div class="site-content">
<div class="brand-info">
<h2 class="brand-info__text">微信后台管理系统</h2>
<p class="brand-info__intro">微信公众号后台管理系统。</p>
</div>
<div class="login-main">
<h3 class="login-title">管理员登录</h3>
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" status-icon>
<el-form-item prop="userName">
<el-input v-model="dataForm.userName" placeholder="帐号"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input>
</el-form-item>
<el-form-item prop="captcha">
<el-row :gutter="20">
<el-col :span="14">
<el-input v-model="dataForm.captcha" placeholder="验证码">
</el-input>
</el-col>
<el-col :span="10" class="login-captcha">
<img :src="captchaPath" @click="getCaptcha()" alt="">
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button class="login-btn-submit" type="primary" @click="dataFormSubmit()">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</template>
<script>
import { getUUID } from '@/utils'
export default {
data () {
return {
dataForm: {
userName: '',
password: '',
uuid: '',
captcha: ''
},
dataRule: {
userName: [
{ required: true, message: '帐号不能为空', trigger: 'blur' }
],
password: [
{ required: true, message: '密码不能为空', trigger: 'blur' }
],
captcha: [
{ required: true, message: '验证码不能为空', trigger: 'blur' }
]
},
captchaPath: ''
}
},
created () {
this.getCaptcha()
},
methods: {
// 提交表单
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl('/sys/login'),
method: 'post',
data: this.$http.adornData({
'username': this.dataForm.userName,
'password': this.dataForm.password,
'uuid': this.dataForm.uuid,
'captcha': this.dataForm.captcha
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$cookie.set('token', data.token)
this.$router.replace({ name: 'home' })
} else {
this.getCaptcha()
this.$message.error(data.msg)
}
})
}
})
},
// 获取验证码
getCaptcha () {
this.dataForm.uuid = getUUID()
this.captchaPath = this.$http.adornUrl(`/captcha.jpg?uuid=${this.dataForm.uuid}`)
}
}
}
</script>
<style lang="scss">
.site-wrapper.site-page--login {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(38, 50, 56, .5);
overflow: hidden;
&:before {
position: fixed;
top: 0;
left: 0;
z-index: -1;
width: 100%;
height: 100%;
content: "";
background-color: #FA8BFF;
background-image: linear-gradient(45deg, #FA8BFF 0%, #2BD2FF 52%, #2BFF88 90%);
background-size: cover;
}
.site-content__wrapper {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 0;
margin: 0;
overflow-x: hidden;
overflow-y: auto;
background-color: transparent;
}
.site-content {
min-height: 100%;
padding: 30px 500px 30px 30px;
}
.brand-info {
margin: 220px 100px 0 90px;
color: #fff;
}
.brand-info__text {
margin: 0 0 22px 0;
font-size: 48px;
font-weight: 400;
text-transform : uppercase;
}
.brand-info__intro {
margin: 10px 0;
font-size: 16px;
line-height: 1.58;
opacity: .6;
}
.login-main {
position: absolute;
top: 0;
right: 0;
padding: 150px 60px 180px;
width: 470px;
min-height: 100%;
background-color: #fff;
}
.login-title {
font-size: 16px;
}
.login-captcha {
overflow: hidden;
> img {
width: 100%;
cursor: pointer;
}
}
.login-btn-submit {
width: 100%;
margin-top: 38px;
}
}
</style>
<template>
<el-form>
<h2>布局设置</h2>
<el-form-item label="导航条类型">
<el-radio-group v-model="navbarLayoutType">
<el-radio label="default" border>default</el-radio>
<el-radio label="inverse" border>inverse</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="侧边栏皮肤">
<el-radio-group v-model="sidebarLayoutSkin">
<el-radio label="light" border>light</el-radio>
<el-radio label="dark" border>dark</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</template>
<script>
export default {
computed: {
navbarLayoutType: {
get () { return this.$store.state.common.navbarLayoutType },
set (val) { this.$store.commit('common/updateNavbarLayoutType', val) }
},
sidebarLayoutSkin: {
get () { return this.$store.state.common.sidebarLayoutSkin },
set (val) { this.$store.commit('common/updateSidebarLayoutSkin', val) }
}
}
}
</script>
<template>
<main class="site-content" :class="{ 'site-content--tabs': $route.meta.isTab }">
<!-- 主入口标签页 s -->
<el-tabs
v-if="$route.meta.isTab"
v-model="mainTabsActiveName"
:closable="true"
@tab-click="selectedTabHandle"
@tab-remove="removeTabHandle">
<el-dropdown class="site-tabs__tools" :show-timeout="0">
<i class="el-icon-arrow-down el-icon--right"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="tabsCloseCurrentHandle">关闭当前标签页</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseOtherHandle">关闭其它标签页</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseAllHandle">关闭全部标签页</el-dropdown-item>
<el-dropdown-item @click.native="refresh()">刷新当前标签页</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-tab-pane
v-for="item in mainTabs"
:key="item.name"
:label="item.title"
:name="item.name">
<el-card :body-style="siteContentViewHeight">
<iframe
v-if="item.type === 'iframe'"
:src="item.iframeUrl"
width="100%" height="100%" frameborder="0" scrolling="yes">
</iframe>
<keep-alive v-else>
<router-view v-if="item.name === mainTabsActiveName" />
</keep-alive>
</el-card>
</el-tab-pane>
</el-tabs>
<!-- 主入口标签页 e -->
<el-card v-else :body-style="siteContentViewHeight">
<keep-alive>
<router-view />
</keep-alive>
</el-card>
</main>
</template>
<script>
import { isURL } from '@/utils/validate'
export default {
inject: ['refresh'],
data () {
return {
}
},
computed: {
documentClientHeight: {
get () { return this.$store.state.common.documentClientHeight }
},
menuActiveName: {
get () { return this.$store.state.common.menuActiveName },
set (val) { this.$store.commit('common/updateMenuActiveName', val) }
},
mainTabs: {
get () { return this.$store.state.common.mainTabs },
set (val) { this.$store.commit('common/updateMainTabs', val) }
},
mainTabsActiveName: {
get () { return this.$store.state.common.mainTabsActiveName },
set (val) { this.$store.commit('common/updateMainTabsActiveName', val) }
},
siteContentViewHeight () {
var height = this.documentClientHeight - 50 - 30 - 2
if (this.$route.meta.isTab) {
height -= 40
return isURL(this.$route.meta.iframeUrl) ? { height: height + 'px' } : { minHeight: height + 'px' }
}
return { minHeight: height + 'px' }
}
},
methods: {
// tabs, 选中tab
selectedTabHandle (tab) {
tab = this.mainTabs.filter(item => item.name === tab.name)
if (tab.length >= 1) {
this.$router.push({ name: tab[0].name, query: tab[0].query, params: tab[0].params })
}
},
// tabs, 删除tab
removeTabHandle (tabName) {
this.$store.commit('common/removeTab', tabName)
},
// tabs, 关闭当前
tabsCloseCurrentHandle () {
this.removeTabHandle(this.mainTabsActiveName)
},
// tabs, 关闭其它
tabsCloseOtherHandle () {
this.mainTabs = this.mainTabs.filter(item => item.name === this.mainTabsActiveName)
},
// tabs, 关闭全部
tabsCloseAllHandle () {
this.mainTabs = []
this.menuActiveName = ''
this.$router.push({ name: 'home' })
},
// tabs, 刷新当前
tabsRefreshCurrentHandle () {
var tab = this.$route
this.removeTabHandle(tab.name)
this.$nextTick(() => {
this.$router.push({ name: tab.name, query: tab.query, params: tab.params })
})
}
}
}
</script>
<template>
<el-dialog
title="修改密码"
:visible.sync="visible"
:append-to-body="true">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="账号">
<span>{{ userName }}</span>
</el-form-item>
<el-form-item label="原密码" prop="password">
<el-input type="password" v-model="dataForm.password"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input type="password" v-model="dataForm.newPassword"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input type="password" v-model="dataForm.confirmPassword"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
import { clearLoginInfo } from '@/utils'
export default {
data () {
var validateConfirmPassword = (rule, value, callback) => {
if (this.dataForm.newPassword !== value) {
callback(new Error('确认密码与新密码不一致'))
} else {
callback()
}
}
return {
visible: false,
dataForm: {
password: '',
newPassword: '',
confirmPassword: ''
},
dataRule: {
password: [
{ required: true, message: '原密码不能为空', trigger: 'blur' }
],
newPassword: [
{ required: true, message: '新密码不能为空', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
{ validator: validateConfirmPassword, trigger: 'blur' }
]
}
}
},
computed: {
userName: {
get () { return this.$store.state.user.name }
},
mainTabs: {
get () { return this.$store.state.common.mainTabs },
set (val) { this.$store.commit('common/updateMainTabs', val) }
}
},
methods: {
// 初始化
init () {
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl('/sys/user/password'),
method: 'post',
data: this.$http.adornData({
'password': this.dataForm.password,
'newPassword': this.dataForm.newPassword
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$nextTick(() => {
this.mainTabs = []
clearLoginInfo()
this.$router.replace({ name: 'login' })
})
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<template>
<nav class="site-navbar" :class="'site-navbar--' + navbarLayoutType">
<div class="site-navbar__header">
<h1 class="site-navbar__brand" @click="$router.push({ name: 'home' })">
<a class="site-navbar__brand-lg" href="javascript:;">微信CMS管理系统</a>
<a class="site-navbar__brand-mini" href="javascript:;">W</a>
</h1>
</div>
<div class="site-navbar__body clearfix">
<el-menu
class="site-navbar__menu"
mode="horizontal">
<el-menu-item class="site-navbar__switch" index="0" @click="sidebarFold = !sidebarFold">
<i :class="sidebarFold?'el-icon-s-unfold':'el-icon-s-fold'" ></i>
</el-menu-item>
</el-menu>
<el-menu
class="site-navbar__menu site-navbar__menu--right"
mode="horizontal">
<el-menu-item index="1" @click="$router.push({ name: 'theme' })">
<template slot="title">
<i class="el-icon-setting"></i>
</template>
</el-menu-item>
<el-menu-item class="site-navbar__avatar" index="3">
<el-dropdown :show-timeout="0" placement="bottom">
<span class="el-dropdown-link">
{{ userName }}
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="updatePasswordHandle()">修改密码</el-dropdown-item>
<el-dropdown-item @click.native="logoutHandle()">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-menu-item>
</el-menu>
</div>
<!-- 弹窗, 修改密码 -->
<update-password v-if="updatePassowrdVisible" ref="updatePassowrd"></update-password>
</nav>
</template>
<script>
import UpdatePassword from './main-navbar-update-password'
import { clearLoginInfo } from '@/utils'
export default {
data () {
return {
updatePassowrdVisible: false
}
},
components: {
UpdatePassword
},
computed: {
navbarLayoutType: {
get () { return this.$store.state.common.navbarLayoutType }
},
sidebarFold: {
get () { return this.$store.state.common.sidebarFold },
set (val) { this.$store.commit('common/updateSidebarFold', val) }
},
mainTabs: {
get () { return this.$store.state.common.mainTabs },
set (val) { this.$store.commit('common/updateMainTabs', val) }
},
userName: {
get () { return this.$store.state.user.name }
}
},
methods: {
// 修改密码
updatePasswordHandle () {
this.updatePassowrdVisible = true
this.$nextTick(() => {
this.$refs.updatePassowrd.init()
})
},
// 退出
logoutHandle () {
this.$confirm(`确定进行[退出]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/logout'),
method: 'post',
data: this.$http.adornData()
}).then(({data}) => {
if (data && data.code === 200) {
clearLoginInfo()
this.$router.push({ name: 'login' })
}
})
}).catch(() => {})
}
}
}
</script>
<template>
<el-submenu
v-if="menu.list && menu.list.length >= 1"
:index="menu.menuId + ''"
:popper-class="'site-sidebar--' + sidebarLayoutSkin + '-popper'">
<template slot="title">
<i class="site-sidebar__menu-icon" :class="menu.icon"></i>
<!-- <icon-svg :name="menu.icon || ''" class="site-sidebar__menu-icon"></icon-svg> -->
<span>{{ menu.name }}</span>
</template>
<sub-menu
v-for="item in menu.list"
:key="item.menuId"
:menu="item"
:dynamicMenuRoutes="dynamicMenuRoutes">
</sub-menu>
</el-submenu>
<el-menu-item v-else :index="menu.menuId + ''" @click="gotoRouteHandle(menu)">
<!-- <icon-svg :name="menu.icon || ''" class="site-sidebar__menu-icon"></icon-svg> -->
<i class="site-sidebar__menu-icon fa" :class="menu.icon"></i>
<span>{{ menu.name }}</span>
</el-menu-item>
</template>
<script>
import SubMenu from './main-sidebar-sub-menu'
export default {
name: 'sub-menu',
props: {
menu: {
type: Object,
required: true
},
dynamicMenuRoutes: {
type: Array,
required: true
}
},
components: {
SubMenu
},
computed: {
sidebarLayoutSkin: {
get () { return this.$store.state.common.sidebarLayoutSkin }
}
},
methods: {
// 通过menuId与动态(菜单)路由进行匹配跳转至指定路由
gotoRouteHandle (menu) {
var route = this.dynamicMenuRoutes.filter(item => item.meta.menuId === menu.menuId)
if (route.length >= 1) {
this.$router.push({ name: route[0].name })
}
}
}
}
</script>
<template>
<aside class="site-sidebar" :class="'site-sidebar--' + sidebarLayoutSkin">
<div class="site-sidebar__inner">
<el-menu
:default-active="menuActiveName || 'home'"
:collapse="sidebarFold"
:collapseTransition="false"
class="site-sidebar__menu">
<el-menu-item index="home" @click="$router.push({ name: 'home' })">
<i class="site-sidebar__menu-icon el-icon-s-home"></i>
<span slot="title">首页</span>
</el-menu-item>
<sub-menu
v-for="menu in menuList"
:key="menu.menuId"
:menu="menu"
:dynamicMenuRoutes="dynamicMenuRoutes">
</sub-menu>
</el-menu>
</div>
</aside>
</template>
<script>
import SubMenu from './main-sidebar-sub-menu'
import { isURL } from '@/utils/validate'
export default {
data () {
return {
dynamicMenuRoutes: []
}
},
components: {
SubMenu
},
computed: {
sidebarLayoutSkin: {
get () { return this.$store.state.common.sidebarLayoutSkin }
},
sidebarFold: {
get () { return this.$store.state.common.sidebarFold }
},
menuList: {
get () { return this.$store.state.common.menuList },
set (val) { this.$store.commit('common/updateMenuList', val) }
},
menuActiveName: {
get () { return this.$store.state.common.menuActiveName },
set (val) { this.$store.commit('common/updateMenuActiveName', val) }
},
mainTabs: {
get () { return this.$store.state.common.mainTabs },
set (val) { this.$store.commit('common/updateMainTabs', val) }
},
mainTabsActiveName: {
get () { return this.$store.state.common.mainTabsActiveName },
set (val) { this.$store.commit('common/updateMainTabsActiveName', val) }
}
},
watch: {
$route: 'routeHandle'
},
created () {
this.menuList = JSON.parse(sessionStorage.getItem('menuList') || '[]')
this.dynamicMenuRoutes = JSON.parse(sessionStorage.getItem('dynamicMenuRoutes') || '[]')
this.routeHandle(this.$route)
},
methods: {
// 路由操作
routeHandle (route) {
if (route.meta.isTab) {
// tab选中, 不存在先添加
var tab = this.mainTabs.filter(item => item.name === route.name)[0]
if (!tab) {
if (route.meta.isDynamic) {
route = this.dynamicMenuRoutes.filter(item => item.name === route.name)[0]
if (!route) {
return console.error('未能找到可用标签页!')
}
}
tab = {
menuId: route.meta.menuId || route.name,
name: route.name,
title: route.meta.title,
type: isURL(route.meta.iframeUrl) ? 'iframe' : 'module',
iframeUrl: route.meta.iframeUrl || '',
params: route.params,
query: route.query
}
this.mainTabs = this.mainTabs.concat(tab)
}
this.menuActiveName = tab.menuId + ''
this.mainTabsActiveName = tab.name
}
}
}
}
</script>
<template>
<div
class="site-wrapper"
:class="{ 'site-sidebar--fold': sidebarFold }"
v-loading.fullscreen.lock="loading"
element-loading-text="拼命加载中">
<template v-if="!loading">
<main-navbar />
<main-sidebar />
<div class="site-content__wrapper" :style="{ 'min-height': documentClientHeight + 'px' }">
<main-content v-if="!$store.state.common.contentIsNeedRefresh" />
</div>
</template>
</div>
</template>
<script>
import MainNavbar from './main-navbar'
import MainSidebar from './main-sidebar'
import MainContent from './main-content'
export default {
provide () {
return {
// 刷新
refresh () {
this.$store.commit('common/updateContentIsNeedRefresh', true)
this.$nextTick(() => {
this.$store.commit('common/updateContentIsNeedRefresh', false)
})
}
}
},
data () {
return {
loading: true
}
},
components: {
MainNavbar,
MainSidebar,
MainContent
},
computed: {
documentClientHeight: {
get () { return this.$store.state.common.documentClientHeight },
set (val) { this.$store.commit('common/updateDocumentClientHeight', val) }
},
sidebarFold: {
get () { return this.$store.state.common.sidebarFold }
},
userId: {
get () { return this.$store.state.user.id },
set (val) { this.$store.commit('user/updateId', val) }
},
userName: {
get () { return this.$store.state.user.name },
set (val) { this.$store.commit('user/updateName', val) }
}
},
created () {
this.getUserInfo()
},
mounted () {
this.resetDocumentClientHeight()
},
methods: {
// 重置窗口可视高度
resetDocumentClientHeight () {
this.documentClientHeight = document.documentElement['clientHeight']
window.onresize = () => {
this.documentClientHeight = document.documentElement['clientHeight']
}
},
// 获取当前管理员信息
getUserInfo () {
this.$http({
url: this.$http.adornUrl('/sys/user/info'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 200) {
this.loading = false
this.userId = data.user.userId
this.userName = data.user.username
}
})
}
}
}
</script>
<template>
<el-dialog
title="云存储配置"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="120px">
<el-form-item size="mini" label="存储类型">
<el-radio-group v-model="dataForm.type">
<el-radio :label="1">七牛</el-radio>
<el-radio :label="2">阿里云</el-radio>
<el-radio :label="3">腾讯云</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="dataForm.type === 1">
<el-form-item label="域名">
<el-input v-model="dataForm.qiniuDomain" placeholder="七牛绑定的域名"></el-input>
</el-form-item>
<el-form-item label="路径前缀">
<el-input v-model="dataForm.qiniuPrefix" placeholder="不设置默认为空"></el-input>
</el-form-item>
<el-form-item label="AccessKey">
<el-input v-model="dataForm.qiniuAccessKey" placeholder="七牛AccessKey"></el-input>
</el-form-item>
<el-form-item label="SecretKey">
<el-input v-model="dataForm.qiniuSecretKey" placeholder="七牛SecretKey"></el-input>
</el-form-item>
<el-form-item label="空间名">
<el-input v-model="dataForm.qiniuBucketName" placeholder="七牛存储空间名"></el-input>
</el-form-item>
</template>
<template v-else-if="dataForm.type === 2">
<el-form-item label="域名">
<el-input v-model="dataForm.aliyunDomain" placeholder="阿里云绑定的域名"></el-input>
</el-form-item>
<el-form-item label="路径前缀">
<el-input v-model="dataForm.aliyunPrefix" placeholder="不设置默认为空"></el-input>
</el-form-item>
<el-form-item label="EndPoint">
<el-input v-model="dataForm.aliyunEndPoint" placeholder="阿里云EndPoint"></el-input>
</el-form-item>
<el-form-item label="AccessKeyId">
<el-input v-model="dataForm.aliyunAccessKeyId" placeholder="阿里云AccessKeyId"></el-input>
</el-form-item>
<el-form-item label="AccessKeySecret">
<el-input v-model="dataForm.aliyunAccessKeySecret" placeholder="阿里云AccessKeySecret"></el-input>
</el-form-item>
<el-form-item label="BucketName">
<el-input v-model="dataForm.aliyunBucketName" placeholder="阿里云BucketName"></el-input>
</el-form-item>
</template>
<template v-else-if="dataForm.type === 3">
<el-form-item label="域名">
<el-input v-model="dataForm.qcloudDomain" placeholder="腾讯云绑定的域名"></el-input>
</el-form-item>
<el-form-item label="路径前缀">
<el-input v-model="dataForm.qcloudPrefix" placeholder="不设置默认为空"></el-input>
</el-form-item>
<el-form-item label="AppId">
<el-input v-model="dataForm.qcloudAppId" placeholder="腾讯云AppId"></el-input>
</el-form-item>
<el-form-item label="SecretId">
<el-input v-model="dataForm.qcloudSecretId" placeholder="腾讯云SecretId"></el-input>
</el-form-item>
<el-form-item label="SecretKey">
<el-input v-model="dataForm.qcloudSecretKey" placeholder="腾讯云SecretKey"></el-input>
</el-form-item>
<el-form-item label="BucketName">
<el-input v-model="dataForm.qcloudBucketName" placeholder="腾讯云BucketName"></el-input>
</el-form-item>
<el-form-item label="Bucket所属地区">
<el-input v-model="dataForm.qcloudRegion" placeholder="如:sh(可选值 ,华南:gz 华北:tj 华东:sh)"></el-input>
</el-form-item>
</template>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data () {
return {
visible: false,
dataForm: {},
dataRule: {}
}
},
methods: {
init (id) {
this.visible = true
this.$http({
url: this.$http.adornUrl('/sys/oss/config'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
this.dataForm = data && data.code === 200 ? data.config : []
})
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl('/sys/oss/saveConfig'),
method: 'post',
data: this.$http.adornData(this.dataForm)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<template>
<div @click="selectFile">
<input type="file" ref="fileInput" v-show="false" @change="onFileChange"/>
<div>{{uploading?infoText:'上传文件'}}</div>
</div>
</template>
<script>
var COS = require('cos-js-sdk-v5');
var cos;
export default {
name: "oss-uploader",
data() {
return {
uploading: false,
infoText:"上传中...",
cosConfig:[]
}
},
mounted(){
this.$http({
url: this.$http.adornUrl('/sys/oss/config'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if(data && data.code === 200){
this.cosConfig = data.config
cos=new COS({
SecretId: data.config.qcloudSecretId,
SecretKey: data.config.qcloudSecretKey,
});
}else{
this.$message.error('请先配置云存储相关信息!')
}
})
},
methods: {
selectFile() {//选择文件
if (!this.uploading) {
this.$refs.fileInput.click();
}
},
onFileChange() {
let file = this.$refs.fileInput.files[0];
this.uploading = true;
let now = new Date();
let path=now.toISOString().slice(0,10)+'/'+now.getTime()+file.name.substr(file.name.lastIndexOf('.'))
cos.putObject({
Bucket: this.cosConfig.qcloudBucketName, /* 必须 */
Region: this.cosConfig.qcloudRegion, /* 必须 */
Key: path, /* 必须 */
Body: file, // 上传文件对象
onProgress: (progressData)=> {
this.infoText='上传中:'+progressData.percent*100+'%'
}
}, (err, data)=> {
console.log(err || data);
this.uploading = false;
if(data){
this.infoText='上传文件'
let fileUrl='https://'+this.cosConfig.qcloudBucketName+'.cos.'+this.cosConfig.qcloudRegion+'.myqcloud.com/'+path;
this.saveUploadResult(fileUrl)
}else {
this.$message.error('文件上传失败',err)
}
});
},
saveUploadResult(url){
this.$http({
url: this.$http.adornUrl('/sys/oss/upload'),
method: 'post',
data:{
url:url
}
}).then(({data})=>{
this.$emit('uploaded', url)
})
}
}
}
</script>
<style scoped>
</style>
<template>
<div class="mod-oss">
<el-form :inline="true" :model="dataForm">
<el-form-item>
<el-button type="primary" @click="configHandle()">云存储配置</el-button>
<el-button type="primary"><OssUploader @uploaded="getDataList"></OssUploader></el-button>
<el-button type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="id"
header-align="center"
align="center"
width="80"
label="ID">
</el-table-column>
<el-table-column
prop="url"
header-align="center"
align="center"
label="URL地址">
<div slot-scope="scope">
<img class="image-sm" v-if="isImageUrl(scope.row.url)" :src="scope.row.url"/>
<a :href="scope.row.url" target="_blank" v-else>{{scope.row.url}}</a>
</div>
</el-table-column>
<el-table-column
prop="createDate"
header-align="center"
align="center"
width="180"
label="创建时间">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteHandle(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 云存储配置 -->
<config v-if="configVisible" ref="config"></config>
<!-- 弹窗, 上传文件 -->
<upload v-if="uploadVisible" ref="upload" @refreshDataList="getDataList"></upload>
</div>
</template>
<script>
export default {
data () {
return {
dataForm: {},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
configVisible: false,
uploadVisible: false
}
},
components: {
Config:()=>import('./oss-config'),
OssUploader:()=>import('./oss-uploader')
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/oss/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'sidx':'id',
'order':'desc'
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 云存储配置
configHandle () {
this.configVisible = true
this.$nextTick(() => {
this.$refs.config.init()
})
},
// 上传文件
uploadHandle () {
this.uploadVisible = true
this.$nextTick(() => {
this.$refs.upload.init()
})
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.id
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/oss/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => {})
},
isImageUrl(url){
return url && /.*\.(gif|jpg|jpeg|png|GIF|JPEG|JPG|PNG)/.test(url)
}
}
}
</script>
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="参数名" prop="paramKey">
<el-input v-model="dataForm.paramKey" placeholder="参数名"></el-input>
</el-form-item>
<el-form-item label="参数值" prop="paramValue">
<el-input v-model="dataForm.paramValue" placeholder="参数值"></el-input>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" placeholder="备注"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data () {
return {
visible: false,
dataForm: {
id: 0,
paramKey: '',
paramValue: '',
remark: ''
},
dataRule: {
paramKey: [
{ required: true, message: '参数名不能为空', trigger: 'blur' }
],
paramValue: [
{ required: true, message: '参数值不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init (id) {
this.dataForm.id = id || 0
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
if (this.dataForm.id) {
this.$http({
url: this.$http.adornUrl(`/sys/config/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 200) {
this.dataForm.paramKey = data.config.paramKey
this.dataForm.paramValue = data.config.paramValue
this.dataForm.remark = data.config.remark
}
})
}
})
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/sys/config/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'id': this.dataForm.id || undefined,
'paramKey': this.dataForm.paramKey,
'paramValue': this.dataForm.paramValue,
'remark': this.dataForm.remark
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<template>
<div class="mod-config">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.paramKey" placeholder="参数名" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="id"
header-align="center"
align="center"
width="80"
label="ID">
</el-table-column>
<el-table-column
prop="paramKey"
header-align="center"
align="center"
label="参数名">
</el-table-column>
<el-table-column
prop="paramValue"
header-align="center"
align="center"
label="参数值">
</el-table-column>
<el-table-column
prop="remark"
header-align="center"
align="center"
label="备注">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)">修改</el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './config-add-or-update'
export default {
data () {
return {
dataForm: {
paramKey: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/config/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'paramKey': this.dataForm.paramKey
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.id
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/config/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => {})
}
}
}
</script>
<template>
<div class="mod-log">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.key" placeholder="用户名/用户操作" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
style="width: 100%">
<el-table-column
prop="id"
header-align="center"
align="center"
width="80"
label="ID">
</el-table-column>
<el-table-column
prop="username"
header-align="center"
align="center"
label="用户名">
</el-table-column>
<el-table-column
prop="operation"
header-align="center"
align="center"
label="用户操作">
</el-table-column>
<el-table-column
prop="method"
header-align="center"
align="center"
width="150"
:show-overflow-tooltip="true"
label="请求方法">
</el-table-column>
<el-table-column
prop="params"
header-align="center"
align="center"
width="150"
:show-overflow-tooltip="true"
label="请求参数">
</el-table-column>
<el-table-column
prop="time"
header-align="center"
align="center"
label="执行时长(毫秒)">
</el-table-column>
<el-table-column
prop="ip"
header-align="center"
align="center"
width="150"
label="IP地址">
</el-table-column>
<el-table-column
prop="createDate"
header-align="center"
align="center"
width="180"
label="创建时间">
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</div>
</template>
<script>
export default {
data () {
return {
dataForm: {
key: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
selectionDataList: []
}
},
created () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/log/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'key': this.dataForm.key,
'sidx':'id',
'order':'desc'
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
}
}
}
</script>
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="类型" prop="type">
<el-radio-group v-model="dataForm.type">
<el-radio v-for="(type, index) in dataForm.typeList" :label="index" :key="index">{{ type }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="dataForm.typeList[dataForm.type] + '名称'" prop="name">
<el-input v-model="dataForm.name" :placeholder="dataForm.typeList[dataForm.type] + '名称'"></el-input>
</el-form-item>
<el-form-item label="上级菜单" prop="parentName">
<el-popover
ref="menuListPopover"
placement="bottom-start"
trigger="click">
<el-tree
:data="menuList"
:props="menuListTreeProps"
node-key="menuId"
ref="menuListTree"
@current-change="menuListTreeCurrentChangeHandle"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false">
</el-tree>
</el-popover>
<el-input v-model="dataForm.parentName" v-popover:menuListPopover :readonly="true" placeholder="点击选择上级菜单" class="menu-list__input"></el-input>
</el-form-item>
<el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url">
<el-input v-model="dataForm.url" placeholder="菜单路由"></el-input>
</el-form-item>
<el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms">
<el-input v-model="dataForm.perms" placeholder="多个用逗号分隔, 如: user:list,user:create"></el-input>
</el-form-item>
<el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon">
<el-row>
<el-col :span="12">
<el-input v-model="dataForm.icon" placeholder="菜单图标名称" class="icon-list__input"></el-input>
</el-col>
<el-col :span="12" class="icon-list__tips">
<el-form-item v-if="dataForm.type !== 2" label="排序号" prop="orderNum">
<el-input-number v-model="dataForm.orderNum" controls-position="right" :min="0" label="排序号"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<div>参考ElementUI图标库, <a href="https://element.eleme.cn/#/zh-CN/component/icon" target="_blank">找图标</a></div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
import { treeDataTranslate } from '@/utils'
export default {
data () {
var validateUrl = (rule, value, callback) => {
if (this.dataForm.type === 1 && !/\S/.test(value)) {
callback(new Error('菜单URL不能为空'))
} else {
callback()
}
}
return {
visible: false,
dataForm: {
id: 0,
type: 1,
typeList: ['目录', '菜单', '按钮'],
name: '',
parentId: 0,
parentName: '',
url: '',
perms: '',
orderNum: 0,
icon: '',
},
dataRule: {
name: [
{ required: true, message: '菜单名称不能为空', trigger: 'blur' }
],
parentName: [
{ required: true, message: '上级菜单不能为空', trigger: 'change' }
],
url: [
{ validator: validateUrl, trigger: 'blur' }
]
},
menuList: [],
menuListTreeProps: {
label: 'name',
children: 'children'
}
}
},
methods: {
init (id) {
this.dataForm.id = id || 0
this.$http({
url: this.$http.adornUrl('/sys/menu/select'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
this.menuList = treeDataTranslate(data.menuList, 'menuId')
}).then(() => {
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
}).then(() => {
if (!this.dataForm.id) {
// 新增
this.menuListTreeSetCurrentNode()
} else {
// 修改
this.$http({
url: this.$http.adornUrl(`/sys/menu/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
this.dataForm.id = data.menu.menuId
this.dataForm.type = data.menu.type
this.dataForm.name = data.menu.name
this.dataForm.parentId = data.menu.parentId
this.dataForm.url = data.menu.url
this.dataForm.perms = data.menu.perms
this.dataForm.orderNum = data.menu.orderNum
this.dataForm.icon = data.menu.icon
this.menuListTreeSetCurrentNode()
})
}
})
},
// 菜单树选中
menuListTreeCurrentChangeHandle (data, node) {
this.dataForm.parentId = data.menuId
this.dataForm.parentName = data.name
},
// 菜单树设置当前选中节点
menuListTreeSetCurrentNode () {
this.$refs.menuListTree.setCurrentKey(this.dataForm.parentId)
this.dataForm.parentName = (this.$refs.menuListTree.getCurrentNode() || {})['name']
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/sys/menu/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'menuId': this.dataForm.id || undefined,
'type': this.dataForm.type,
'name': this.dataForm.name,
'parentId': this.dataForm.parentId,
'url': this.dataForm.url,
'perms': this.dataForm.perms,
'orderNum': this.dataForm.orderNum,
'icon': this.dataForm.icon
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<style lang="scss">
.mod-menu {
.menu-list__input,
.icon-list__input {
> .el-input__inner {
cursor: pointer;
}
}
&__icon-popover {
width: 458px;
overflow: hidden;
}
&__icon-inner {
width: 478px;
max-height: 258px;
overflow-x: hidden;
overflow-y: auto;
}
&__icon-list {
width: 458px;
padding: 0;
margin: -8px 0 0 -8px;
> .el-button {
padding: 8px;
margin: 8px 0 0 8px;
> span {
display: inline-block;
vertical-align: middle;
width: 18px;
height: 18px;
font-size: 18px;
}
}
}
.icon-list__tips {
font-size: 18px;
text-align: center;
color: #e6a23c;
cursor: pointer;
}
}
</style>
<template>
<div class="mod-menu">
<el-form :inline="true" :model="dataForm">
<el-form-item>
<el-button v-if="isAuth('sys:menu:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
row-key="menuId"
border
style="width: 100%; ">
<el-table-column
prop="name"
header-align="center"
min-width="150"
label="名称" >
</el-table-column>
<el-table-column
prop="parentName"
header-align="center"
align="center"
width="120"
label="上级菜单">
</el-table-column>
<el-table-column
header-align="center"
align="center"
label="图标">
<template slot-scope="scope">
<i :class="scope.row.icon"></i>
</template>
</el-table-column>
<el-table-column
prop="type"
header-align="center"
align="center"
label="类型">
<template slot-scope="scope">
<el-tag v-if="scope.row.type === 0" size="small">目录</el-tag>
<el-tag v-else-if="scope.row.type === 1" size="small" type="success">菜单</el-tag>
<el-tag v-else-if="scope.row.type === 2" size="small" type="info">按钮</el-tag>
</template>
</el-table-column>
<el-table-column
prop="orderNum"
header-align="center"
align="center"
label="排序号">
</el-table-column>
<el-table-column
prop="url"
header-align="center"
align="center"
width="150"
:show-overflow-tooltip="true"
label="菜单URL">
</el-table-column>
<el-table-column
prop="perms"
header-align="center"
align="center"
width="150"
:show-overflow-tooltip="true"
label="授权标识">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button v-if="isAuth('sys:menu:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.menuId)">修改</el-button>
<el-button v-if="isAuth('sys:menu:delete')" type="text" size="small" @click="deleteHandle(scope.row.menuId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './menu-add-or-update'
import { treeDataTranslate } from '@/utils'
export default {
data () {
return {
dataForm: {},
dataList: [],
dataListLoading: false,
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/menu/list'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
this.dataList = treeDataTranslate(data, 'menuId')
this.dataListLoading = false
})
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
this.$confirm(`确定对[id=${id}]进行[删除]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl(`/sys/menu/delete/${id}`),
method: 'post',
data: this.$http.adornData()
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => {})
}
}
}
</script>
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="dataForm.roleName" placeholder="角色名称"></el-input>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" placeholder="备注"></el-input>
</el-form-item>
<el-form-item size="mini" label="授权">
<el-tree
:data="menuList"
:props="menuListTreeProps"
node-key="menuId"
ref="menuListTree"
:default-expand-all="true"
show-checkbox>
</el-tree>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
import { treeDataTranslate } from '@/utils'
export default {
data () {
return {
visible: false,
menuList: [],
menuListTreeProps: {
label: 'name',
children: 'children'
},
dataForm: {
id: 0,
roleName: '',
remark: ''
},
dataRule: {
roleName: [
{ required: true, message: '角色名称不能为空', trigger: 'blur' }
]
},
tempKey: -666666 // 临时key, 用于解决tree半选中状态项不能传给后台接口问题. # 待优化
}
},
methods: {
init (id) {
this.dataForm.id = id || 0
this.$http({
url: this.$http.adornUrl('/sys/menu/list'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
this.menuList = treeDataTranslate(data, 'menuId')
}).then(() => {
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
this.$refs.menuListTree.setCheckedKeys([])
})
}).then(() => {
if (this.dataForm.id) {
this.$http({
url: this.$http.adornUrl(`/sys/role/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 200) {
this.dataForm.roleName = data.role.roleName
this.dataForm.remark = data.role.remark
var idx = data.role.menuIdList.indexOf(this.tempKey)
if (idx !== -1) {
data.role.menuIdList.splice(idx, data.role.menuIdList.length - idx)
}
this.$refs.menuListTree.setCheckedKeys(data.role.menuIdList)
}
})
}
})
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/sys/role/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'roleId': this.dataForm.id || undefined,
'roleName': this.dataForm.roleName,
'remark': this.dataForm.remark,
'menuIdList': [].concat(this.$refs.menuListTree.getCheckedKeys(), [this.tempKey], this.$refs.menuListTree.getHalfCheckedKeys())
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<template>
<div class="mod-role">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.roleName" placeholder="角色名称" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button v-if="isAuth('sys:role:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('sys:role:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="roleId"
header-align="center"
align="center"
width="80"
label="ID">
</el-table-column>
<el-table-column
prop="roleName"
header-align="center"
align="center"
label="角色名称">
</el-table-column>
<el-table-column
prop="remark"
header-align="center"
align="center"
label="备注">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button v-if="isAuth('sys:role:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.roleId)">修改</el-button>
<el-button v-if="isAuth('sys:role:delete')" type="text" size="small" @click="deleteHandle(scope.row.roleId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './role-add-or-update'
export default {
data () {
return {
dataForm: {
roleName: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/role/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'roleName': this.dataForm.roleName
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.roleId
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/role/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => {})
}
}
}
</script>
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="用户名" prop="userName">
<el-input v-model="dataForm.userName" placeholder="登录帐号"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password" :class="{ 'is-required': !dataForm.id }">
<el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="comfirmPassword" :class="{ 'is-required': !dataForm.id }">
<el-input v-model="dataForm.comfirmPassword" type="password" placeholder="确认密码"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="dataForm.email" placeholder="邮箱"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="dataForm.mobile" placeholder="手机号"></el-input>
</el-form-item>
<el-form-item label="角色" size="mini" prop="roleIdList">
<el-checkbox-group v-model="dataForm.roleIdList">
<el-checkbox v-for="role in roleList" :key="role.roleId" :label="role.roleId">{{ role.roleName }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="状态" size="mini" prop="status">
<el-radio-group v-model="dataForm.status">
<el-radio :label="0">禁用</el-radio>
<el-radio :label="1">正常</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
import { isEmail, isMobile } from '@/utils/validate'
export default {
data () {
var validatePassword = (rule, value, callback) => {
if (!this.dataForm.id && !/\S/.test(value)) {
callback(new Error('密码不能为空'))
} else {
callback()
}
}
var validateComfirmPassword = (rule, value, callback) => {
if (!this.dataForm.id && !/\S/.test(value)) {
callback(new Error('确认密码不能为空'))
} else if (this.dataForm.password !== value) {
callback(new Error('确认密码与密码输入不一致'))
} else {
callback()
}
}
var validateEmail = (rule, value, callback) => {
if (!isEmail(value)) {
callback(new Error('邮箱格式错误'))
} else {
callback()
}
}
var validateMobile = (rule, value, callback) => {
if (!isMobile(value)) {
callback(new Error('手机号格式错误'))
} else {
callback()
}
}
return {
visible: false,
roleList: [],
dataForm: {
id: 0,
userName: '',
password: '',
comfirmPassword: '',
salt: '',
email: '',
mobile: '',
roleIdList: [],
status: 1
},
dataRule: {
userName: [
{ required: true, message: '用户名不能为空', trigger: 'blur' }
],
password: [
{ validator: validatePassword, trigger: 'blur' }
],
comfirmPassword: [
{ validator: validateComfirmPassword, trigger: 'blur' }
],
email: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
{ validator: validateEmail, trigger: 'blur' }
],
mobile: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{ validator: validateMobile, trigger: 'blur' }
]
}
}
},
methods: {
init (id) {
this.dataForm.id = id || 0
this.$http({
url: this.$http.adornUrl('/sys/role/select'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
this.roleList = data && data.code === 200 ? data.list : []
}).then(() => {
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
}).then(() => {
if (this.dataForm.id) {
this.$http({
url: this.$http.adornUrl(`/sys/user/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 200) {
this.dataForm.userName = data.user.username
this.dataForm.salt = data.user.salt
this.dataForm.email = data.user.email
this.dataForm.mobile = data.user.mobile
this.dataForm.roleIdList = data.user.roleIdList
this.dataForm.status = data.user.status
}
})
}
})
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/sys/user/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'userId': this.dataForm.id || undefined,
'username': this.dataForm.userName,
'password': this.dataForm.password,
'salt': this.dataForm.salt,
'email': this.dataForm.email,
'mobile': this.dataForm.mobile,
'status': this.dataForm.status,
'roleIdList': this.dataForm.roleIdList
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<template>
<div class="mod-user">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.userName" placeholder="用户名" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button v-if="isAuth('sys:user:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('sys:user:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="userId"
header-align="center"
align="center"
width="80"
label="ID">
</el-table-column>
<el-table-column
prop="username"
header-align="center"
align="center"
label="用户名">
</el-table-column>
<el-table-column
prop="email"
header-align="center"
align="center"
label="邮箱">
</el-table-column>
<el-table-column
prop="mobile"
header-align="center"
align="center"
label="手机号">
</el-table-column>
<el-table-column
prop="status"
header-align="center"
align="center"
label="状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 0" size="small" type="danger">禁用</el-tag>
<el-tag v-else size="small">正常</el-tag>
</template>
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button v-if="isAuth('sys:user:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.userId)">修改</el-button>
<el-button v-if="isAuth('sys:user:delete')" type="text" size="small" @click="deleteHandle(scope.row.userId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './user-add-or-update'
export default {
data () {
return {
dataForm: {
userName: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/user/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'username': this.dataForm.userName
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
var userIds = id ? [id] : this.dataListSelections.map(item => {
return item.userId
})
this.$confirm(`确定对[id=${userIds.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/user/delete'),
method: 'post',
data: this.$http.adornData(userIds, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => {})
}
}
}
</script>
<template>
<div>
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="文章标题" prop="title" required>
<el-input v-model="dataForm.title" :maxlength="1024" placeholder="标题"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="文章类型" prop="type" required>
<el-select v-model="dataForm.type" placeholder="选择文章类型">
<el-option
v-for="(name,key) in ARTICLE_TYPES"
:key="name"
:label="name"
:value="key"
allow-create
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="一级目录" prop="category">
<el-select v-model="dataForm.category" placeholder="一级目录" allow-create>
<el-option
v-for="name in ARTICLE_CATEGORIES"
:key="name"
:label="name"
:value="name"
allow-create
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="二级分类" prop="subCategory">
<el-input :maxlength="50" v-model="dataForm.subCategory" placeholder="二级目录"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="dataForm.type>=2 && dataForm.type<=4">
<el-col :span="12">
<el-form-item label="生效时间" prop="startTime">
<el-date-picker
v-model="dataForm.startTime"
type="datetime"
default-time="00:00:00"
placeholder="选择日期时间"
value-format="timestamp"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="失效时间" prop="endTime">
<el-date-picker
v-model="dataForm.endTime"
type="datetime"
default-time="22:00:00"
placeholder="选择日期时间"
value-format="timestamp"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="指向外链" prop="targetLink">
<el-input v-model="dataForm.targetLink" placeholder="指向外链"></el-input>
</el-form-item>
<el-form-item label="摘要" prop="summary" v-if="dataForm.type==1 || dataForm.type==5">
<el-input
v-model="dataForm.summary"
placeholder="摘要"
type="textarea"
rows="3"
maxlength="512"
show-word-limit
></el-input>
</el-form-item>
<el-form-item label="标签" prop="tags">
<tags-editor v-model="dataForm.tags"></tags-editor>
</el-form-item>
<el-row>
<el-form-item
label="图片地址"
prop="image"
:required="dataForm.type==4"
v-if="dataForm.type==4"
>
<el-input v-model="dataForm.image" placeholder="图片链接"></el-input>
<el-upload
class="upload-demo"
list-type="picture"
action="/wx/upload/uploadImg"
:data="{'savePath':'article'}"
accept="image/*"
:limit="1"
:on-success="imgUploadSuccess"
>
<el-button size="small" type="primary">上传图片</el-button>
</el-upload>
</el-form-item>
</el-row>
<tinymce-editor ref="editor" v-model="dataForm.content"></tinymce-editor>
</el-form>
<div class="footer">
<el-button @click="closeCurrentTab">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</div>
</div>
</template>
<script>
let todayStart = new Date(new Date().toLocaleDateString());
export default {
components: {
TinymceEditor: () => import("@/components/tinymce-editor"),
tagsEditor: () => import("@/components/tags-editor")
},
data() {
return {
dataForm: {
id: "",
type: "4",
title: "",
content: "",
category: "公告",
subCategory: "",
summary: "",
tags: "",
createTime: "",
updateTime: "",
openCount: 0,
startTime: todayStart,
endTime: new Date(
todayStart.getTime() + 24 * 60 * 60 * 1000 - 1000
),
targetLink: location.origin + "/client/#/article/${articleId}",
image: ""
},
dataRule: {
type: [
{
required: true,
message: "文章类型不能为空",
trigger: "blur"
}
],
title: [
{ required: true, message: "标题不能为空", trigger: "blur" }
],
category: [
{ required: true, message: "分类不能为空", trigger: "blur" }
]
}
};
},
computed: {
ARTICLE_TYPES: {
get() {
return this.$store.state.article.ARTICLE_TYPES;
}
},
ARTICLE_CATEGORIES: {
get() {
return this.$store.state.article.ARTICLE_CATEGORIES;
}
}
},
mounted() {
let id = this.$route.query.id;
this.init(id);
},
methods: {
init(id) {
this.dataForm.id = id || "";
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
if (id > 0) {
this.$http({
url: this.$http.adornUrl(
`/manage/article/info/${this.dataForm.id}`
),
method: "get",
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataForm.type = data.article.type + "";
this.dataForm.title = data.article.title;
this.dataForm.content = data.article.content;
this.dataForm.category = data.article.category;
this.dataForm.summary = data.article.summary;
this.dataForm.tags = data.article.tags;
this.dataForm.subCategory =
data.article.subCategory;
this.dataForm.createTime = data.article.createTime;
this.dataForm.updateTime = data.article.updateTime;
this.dataForm.openCount = data.article.openCount;
this.dataForm.startTime = data.article.startTime;
this.dataForm.endTime = data.article.endTime;
this.dataForm.targetLink = data.article.targetLink;
this.dataForm.image = data.article.image;
}
});
}
});
},
// 表单提交
dataFormSubmit() {
this.$refs["dataForm"].validate(valid => {
if (valid) {
this.$http({
url: this.$http.adornUrl(
`/manage/article/${
!this.dataForm.id ? "save" : "update"
}`
),
method: "post",
data: this.$http.adornData({
id: this.dataForm.id || undefined,
type: this.dataForm.type,
title: this.dataForm.title,
content: this.dataForm.content,
category: this.dataForm.category,
summary: this.dataForm.summary,
tags: this.dataForm.tags,
subCategory: this.dataForm.subCategory,
createTime:
this.dataForm.createTime ||
new Date().getTime(),
updateTime: new Date().getTime(),
openCount: this.dataForm.openCount,
startTime: this.dataForm.startTime,
endTime: this.dataForm.endTime,
targetLink: this.dataForm.targetLink,
image: this.dataForm.image
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.closeCurrentTab();
}
});
} else {
this.$message.error(data.msg);
}
});
}
});
},
imgUploadSuccess(response, file, fileList) {
console.log(response);
if (response.code == 200) {
this.dataForm.image = response.data;
console.log("this.article", this.article);
} else {
this.$message({ type: "warning", message: response.msg });
}
},
closeCurrentTab() {
this.$store.commit("common/closeCurrentTab");
}
}
};
</script>
<style scoped>
.footer {
margin-top: 20px;
}
</style>
\ No newline at end of file
<template>
<div class="mod-config">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-select v-model="dataForm.type" placeholder="选择文章类型">
<el-option v-for="(name,key) in ARTICLE_TYPES" :key="name" :label="name" :value="key" allow-create></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="dataForm.title" placeholder="标题" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="pageIndex=1;getDataList()">查询</el-button>
<el-button v-if="isAuth('wx:article:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('wx:article:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="id"
header-align="center"
align="center"
label="ID">
</el-table-column>
<el-table-column
prop="type"
header-align="center"
align="center"
label="文章类型">
</el-table-column>
<el-table-column
prop="title"
header-align="center"
align="center"
show-overflow-tooltip
label="标题">
<a :href="scope.row.targetLink" slot-scope="scope">{{scope.row.title}}</a>
</el-table-column>
<el-table-column
prop="category"
header-align="center"
align="center"
label="一级分类">
</el-table-column>
<el-table-column
prop="subCategory"
header-align="center"
align="center"
label="二级分类">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)">修改</el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</div>
</template>
<script>
export default {
data () {
return {
dataForm: {
title: '',
type:''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
computed:{
ARTICLE_TYPES:{
get(){
return this.$store.state.article.ARTICLE_TYPES
}
}
},
components: {
},
activated () {
this.getDataList()
console.log(this.ARTICLE_TYPES)
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/manage/article/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'title': this.dataForm.title,
'type': this.dataForm.type,
'sidx':'id',
'order':'desc'
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
// this.addOrUpdateVisible = true
// this.$nextTick(() => {
// this.$refs.addOrUpdate.init(id)
// })
id = id || ''
this.$router.push('/article-add-or-update?id='+id)
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.id
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/article/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
})
},
articleTypeFormat(row, column, cellValue) {
return this.ARTICLE_TYPES[cellValue];
}
}
}
</script>
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="媒体文件">
<el-button type="primary">
选择文件
<input type="file" style="opacity: 0;height: 100%;position: absolute;left: 0;top: 0;" @change="onFileChange"/>
</el-button>
<div>{{dataForm.file.name}}</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="媒体类型" prop="mediaType">
<el-select v-model="dataForm.mediaType" placeholder="媒体类型" style="width:100%">
<el-option label="图片" value="image"></el-option>
<el-option label="视频" value="video"></el-option>
<el-option label="语音" value="voice"></el-option>
<el-option label="缩略图(64K以内JPG)" value="thumb"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="素材名称" prop="fileName">
<el-input v-model="dataForm.fileName" placeholder="素材名称"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data () {
return {
visible: false,
dataForm: {
mediaId: '',
file:'',
fileName: '',
mediaType: 'image'
},
dataRule: {
fileName: [
{ required: true, message: '素材名称不能为空', trigger: 'blur' }
],
mediaType: [
{ required: true, message: '素材类型不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init (id) {
this.dataForm.mediaId = id || ""
this.visible = true
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
console.log(this.dataForm)
let form = new FormData();
form.append('mediaId',this.dataForm.mediaId || '')
form.append('file', this.dataForm.file)
form.append('fileName', this.dataForm.fileName)
form.append('mediaType', this.dataForm.mediaType)
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialFileUpload`),
method: 'post',
data: form,
headers: {'Content-Type':'multipart/form-data'}
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
},
onFileChange(e){
let file = event.currentTarget.files[0]
this.dataForm.file = file;
this.dataForm.fileName=file.name.substring(0,file.name.lastIndexOf('.'))
let mediaType=file.type.substring(0,file.type.lastIndexOf('/'))
if(mediaType=='audio')mediaType='voice'
this.dataForm.mediaType=mediaType
}
}
}
</script>
<template>
<div class="mod-menu">
<el-form :inline="true" :model="dataForm">
<el-form-item>
<el-button type="primary" @click="addOrUpdateHandle()">新增</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
style="width: 100%;">
<el-table-column
prop="mediaId"
header-align="center"
align="center"
label="素材mediaId">
</el-table-column>
<el-table-column
prop="name"
header-align="center"
align="center"
label="素材名称">
</el-table-column>
<el-table-column
prop="url"
header-align="center"
align="center"
show-overflow-tooltip
label="链接地址">
<a :href="scope.row.url" slot-scope="scope">{{scope.row.url}}</a>
</el-table-column>
<el-table-column
prop="updateTime"
header-align="center"
align="center"
label="更新时间">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.mediaId)">修改</el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.mediaId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-size="pageSize"
:total="totalCount"
layout="total, prev,pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="materialFileBatchGet"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './material-file-add-or-update'
export default {
components:{
AddOrUpdate
},
data(){
return{
dataForm:{},
addOrUpdateVisible: false,
dataList: [],
pageIndex: 1,
pageSize: 20,
totalCount: 0,
dataListLoading: false,
}
},
mounted(){
this.materialFileBatchGet()
},
methods:{
materialFileBatchGet(){
this.dataListLoading=true
this.$http({
url:this.$http.adornUrl('/manage/wxAssets/materialFileBatchGet'),
params: this.$http.adornParams({
'page': this.pageIndex
})
}).then(({data})=>{
this.dataListLoading=false
if(data && data.code==200){
this.dataList=data.data.items
this.totalCount=data.data.totalCount
}else{
this.$message({
type: 'error',
message: data.msg
});
}
})
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
//删除
deleteHandle (id) {
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'),
method: 'post',
data: {mediaId:id}
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.materialFileBatchGet()
}
})
} else {
this.$message.error(data.msg)
}
})
})
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
}
}
}
</script>
\ No newline at end of file
<template>
<div >
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="120px">
<el-form-item label="标题" prop="title">
<el-input v-model="dataForm.title" placeholder="标题"></el-input>
</el-form-item>
<el-form-item label="链接" prop="url">
<el-input v-model="dataForm.url" placeholder="链接"></el-input>
</el-form-item>
<el-form-item label="阅读原文链接" prop="contentSourceUrl">
<el-input v-model="dataForm.contentSourceUrl" placeholder="阅读原文链接"></el-input>
</el-form-item>
<el-form-item label="描述" prop="digest">
<el-input v-model="dataForm.digest" placeholder="描述"></el-input>
</el-form-item>
<el-form-item label="media_id" prop="thumbMediaId">
<el-input v-model="dataForm.thumbMediaId" placeholder="封面图media_id"></el-input>
</el-form-item>
<el-form-item label="内容" prop="content">
<tinymce-editor ref="editor" v-model="dataForm.content"> </tinymce-editor>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeCurrentTab">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</div>
</template>
<script>
export default {
components:{
TinymceEditor:()=>import('@/components/tinymce-editor')
},
data () {
return {
dataForm: {
templateId: 0,
title: '',
content: '',
showCoverPic:true,
url: '',
contentSourceUrl: '',
digest: '',
thumbMediaId: ''
},
dataRule: {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
content: [
{ required: true, message: '内容不能为空', trigger: 'blur' }
],
thumbMediaId: [
{ required: true, message: '封面图media_id不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init (id) {
this.dataForm.mediaId = id || ""
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialNewsUpload`),
method: 'post',
data: this.$http.adornData({
'title': this.dataForm.title,
'content': this.dataForm.content,
'url': this.dataForm.url,
'contentSourceUrl': this.dataForm.contentSourceUrl,
'digest': this.dataForm.digest,
'thumbMediaId': this.dataForm.thumbMediaId
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.$emit('refreshDataList')
this.closeCurrentTab();
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
},
closeCurrentTab() {
this.$store.commit("common/closeCurrentTab");
}
}
}
</script>
<template>
<div class="mod-menu">
<el-form :inline="true" :model="dataForm">
<el-form-item>
<el-button v-if="isAuth('wx:material:save')" type="primary" @click="addOrUpdateHandle">新增</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
style="width: 100%;">
<el-table-column
prop="mediaId"
header-align="center"
align="center"
label="素材mediaId">
</el-table-column>
<el-table-column
prop="content"
header-align="center"
align="center"
label="首篇图文">
<a :href="scope.row.content.articles[0].url" slot-scope="scope">{{scope.row.content.articles[0].title}}</a>
</el-table-column>
<el-table-column
prop="content"
header-align="center"
align="center"
label="图文数量">
<span slot-scope="scope">{{scope.row.content.articles.length}}</span>
</el-table-column>
<el-table-column
prop="updateTime"
header-align="center"
align="center"
label="更新时间">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteHandle(scope.row.mediaId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-size="pageSize"
:total="totalCount"
layout="total, prev,pager, next, jumper">
</el-pagination>
</div>
</template>
<script>
export default {
data(){
return{
dataForm:{},
addOrUpdateVisible: false,
dataList: [],
pageIndex: 1,
pageSize: 20,
totalCount: 0,
dataListLoading: false
}
},
mounted(){
this.materialNewsBatchGet()
},
methods:{
materialNewsBatchGet(){
this.dataListLoading=true
this.$http({
url:this.$http.adornUrl('/manage/wxAssets/materialNewsBatchGet'),
params: this.$http.adornParams({
'page': this.pageIndex
})
}).then(({data})=>{
this.dataListLoading=false
if(data.code==200){
this.dataList=data.data.items
this.totalCount=data.data.totalCount
}else{
this.$message({
type: 'error',
message: data.msg
});
}
})
},
loadTree(tree, treeNode, resolve){
console.log(tree,treeNode)
},
//删除
deleteHandle (id) {
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'),
method: 'post',
data: {mediaId:id}
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.materialFileBatchGet()
}
})
} else {
this.$message.error(data.msg)
}
})
})
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 新增 / 修改
addOrUpdateHandle (id) {
// id = id || ''
// this.$router.push('/material-news-add-or-update?id='+id)
this.$message.info("请前往公众号后台编辑!")
setTimeout(()=>window.open('https://mp.weixin.qq.com/'),1500)
}
}
}
</script>
\ No newline at end of file
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible"
>
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
label-width="80px"
>
<el-form-item label="规则名称" prop="ruleName">
<el-input v-model="dataForm.ruleName" placeholder="规则名称"></el-input>
</el-form-item>
<el-form-item label="匹配词" prop="matchValue">
<tags-editor v-model="dataForm.matchValue"></tags-editor>
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="回复类型" prop="replyType">
<el-select v-model="dataForm.replyType" @change="onReplyTypeChange">
<el-option v-for="(name,key) in KefuMsgType" :key="key" :value="key" :label="name"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否启用" prop="status">
<el-switch v-model="dataForm.status" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="生效时间" prop="effectTimeStart">
<el-time-picker v-model="dataForm.effectTimeStart" value-format="HH:mm:ss"></el-time-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="失效时间" prop="effectTimeEnd">
<el-time-picker v-model="dataForm.effectTimeEnd" value-format="HH:mm:ss"></el-time-picker>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="回复内容" prop="replyContent">
<el-input v-model="dataForm.replyContent" type="textarea" :rows="5" placeholder="文本、图文ID、media_id、json配置"></el-input>
<el-button type="text" v-show="'text'==dataForm.replyType" @click="addLink">插入链接</el-button>
</el-form-item>
<el-form-item label="备注说明" prop="desc">
<el-input v-model="dataForm.desc" placeholder="备注说明"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
components:{
tagsEditor:()=>import('@/components/tags-editor')
},
data() {
return {
visible: false,
dataForm: {
ruleId: 0,
ruleName: "",
matchValue: "",
replyType: 'text',
replyContent: "",
status: 1,
desc: "",
effectTimeStart: "00:00:00",
effectTimeEnd: "23:59:59"
},
dataRule: {
ruleName: [
{ required: true, message: "规则名称不能为空", trigger: "blur" }
],
matchValue: [
{
required: true,
message: "匹配的关键词、事件等不能为空",
trigger: "blur"
}
],
replyType: [
{
required: true,
message: "回复类型(1:文本2:图文3媒体)不能为空",
trigger: "blur"
}
],
replyContent: [
{ required: true, message: "回复内容不能为空", trigger: "blur" }
],
status: [
{ required: true, message: "是否有效不能为空", trigger: "blur" }
],
effectTimeStart: [
{ required: true, message: "生效起始时间不能为空", trigger: "blur" }
],
effectTimeEnd: [
{ required: true, message: "生效结束时间不能为空", trigger: "blur" }
]
}
};
},
computed:{
KefuMsgType:{
get(){
return this.$store.state.message.KefuMsgType
}
}
},
methods: {
init(id) {
this.dataForm.ruleId = id || 0;
this.visible = true;
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
if (this.dataForm.ruleId) {
this.$http({
url: this.$http.adornUrl(
`/manage/msgreplyrule/info/${this.dataForm.ruleId}`
),
method: "get",
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataForm.ruleName = data.msgReplyRule.ruleName;
this.dataForm.matchValue = data.msgReplyRule.matchValue;
this.dataForm.replyType = data.msgReplyRule.replyType;
this.dataForm.replyContent = data.msgReplyRule.replyContent;
this.dataForm.status = data.msgReplyRule.status;
this.dataForm.desc = data.msgReplyRule.desc;
this.dataForm.effectTimeStart = data.msgReplyRule.effectTimeStart;
this.dataForm.effectTimeEnd = data.msgReplyRule.effectTimeEnd;
}
});
}
});
},
// 表单提交
dataFormSubmit() {
this.$refs["dataForm"].validate(valid => {
if (valid) {
this.$http({
url: this.$http.adornUrl(
`/manage/msgreplyrule/${
!this.dataForm.ruleId ? "save" : "update"
}`
),
method: "post",
data: this.$http.adornData({
ruleId: this.dataForm.ruleId || undefined,
ruleName: this.dataForm.ruleName,
matchValue: this.dataForm.matchValue,
replyType: this.dataForm.replyType,
replyContent: this.dataForm.replyContent,
status: this.dataForm.status,
desc: this.dataForm.desc,
effectTimeStart: this.dataForm.effectTimeStart,
effectTimeEnd: this.dataForm.effectTimeEnd
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.visible = false;
this.$emit("refreshDataList");
}
});
} else {
this.$message.error(data.msg);
}
});
}
});
},
addLink(){
this.dataForm.replyContent+='<a href="链接地址">链接文字</a>'
},
onReplyTypeChange(value){
if("miniprogrampage"==value){
let demo={title: "标题", appid: "小程序APPID", pagepath: "页面地址", thumb_media_id: "缩略图media_id"};
this.dataForm.replyContent=JSON.stringify(demo,null,4)
}else if("music"==value){
let demo={musicurl: "音乐链接", hqmusicurl: "高品质链接", title: "标题", description: "描述", thumb_media_id: "缩略图media_id"}
this.dataForm.replyContent=JSON.stringify(demo,null,4)
}else if("msgmenu"==value){
let demo={head_content: "开头文字", list: [{id: "菜单1ID", content: "菜单2内容"},{id: "菜单2ID", content: "菜单2内容"},{id: "菜单nID", content: "菜单n内容"}], tail_content: "结尾文字"}
this.dataForm.replyContent=JSON.stringify(demo,null,4)
}else{
this.dataForm.replyContent=''
}
}
}
};
</script>
<template>
<div class="mod-config">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.matchValue" placeholder="匹配关键词" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button v-if="isAuth('wx:msgreplyrule:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('wx:msgreplyrule:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
type="expand"
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="ID">
<span>{{ props.row.ruleId }}</span>
</el-form-item>
<el-form-item label="是否有效">
<span>{{ props.row.status }}</span>
</el-form-item>
<el-form-item label="备注说明">
<span>{{ props.row.desc }}</span>
</el-form-item>
<el-form-item label="生效时间">
<span>{{ props.row.effectTimeStart }}</span>
</el-form-item>
<el-form-item label="失效时间">
<span>{{ props.row.effectTimeEnd }}</span>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="ruleName"
header-align="center"
align="center"
show-overflow-tooltip
label="规则名称">
</el-table-column>
<el-table-column
prop="matchValue"
header-align="center"
align="center"
show-overflow-tooltip
label="匹配关键词">
</el-table-column>
<el-table-column
prop="replyType"
header-align="center"
align="center"
:formatter="replyTypeFormat"
label="消息类型">
</el-table-column>
<el-table-column
prop="replyContent"
header-align="center"
align="center"
show-overflow-tooltip
label="回复内容">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.ruleId)">修改</el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.ruleId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './msgreplyrule-add-or-update'
export default {
components: {
AddOrUpdate
},
data () {
return {
dataForm: {
matchValue: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
computed:{
KefuMsgType:{
get(){
return this.$store.state.message.KefuMsgType
}
}
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/manage/msgreplyrule/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'matchValue': this.dataForm.matchValue,
'sidx':'rule_id',
'order':'desc'
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.ruleId
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/msgreplyrule/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
})
},
replyTypeFormat(row, column, cellValue) {
return this.KefuMsgType[cellValue];
}
}
}
</script>
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="80px">
<el-form-item label="标题" prop="title">
<el-input v-model="dataForm.title" placeholder="标题"></el-input>
</el-form-item>
<el-form-item label="内容" prop="data">
<el-input v-model="dataForm.data" placeholder="内容"></el-input>
</el-form-item>
<el-form-item label="链接" prop="url">
<el-input v-model="dataForm.url" placeholder="链接"></el-input>
</el-form-item>
<el-form-item label="颜色" prop="color">
<el-input v-model="dataForm.color" placeholder="颜色"></el-input>
</el-form-item>
<el-form-item label="是否有效" prop="status">
<el-input v-model="dataForm.status" placeholder="是否有效"></el-input>
</el-form-item>
<el-form-item label="模版名称" prop="name">
<el-input v-model="dataForm.name" placeholder="模版名称"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data () {
return {
visible: false,
dataForm: {
templateId: 0,
title: '',
data: '',
url: '',
color: '',
status: '',
name: ''
},
dataRule: {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
data: [
{ required: true, message: '内容不能为空', trigger: 'blur' }
],
url: [
{ required: true, message: '链接不能为空', trigger: 'blur' }
],
color: [
{ required: true, message: '颜色不能为空', trigger: 'blur' }
],
status: [
{ required: true, message: '是否有效不能为空', trigger: 'blur' }
],
name: [
{ required: true, message: '模版名称不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init (id) {
this.dataForm.templateId = id || 0
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
if (this.dataForm.templateId) {
this.$http({
url: this.$http.adornUrl(`/manage/msgtemplate/info/${this.dataForm.templateId}`),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 200) {
this.dataForm.title = data.msgTemplate.title
this.dataForm.data = data.msgTemplate.data
this.dataForm.url = data.msgTemplate.url
this.dataForm.color = data.msgTemplate.color
this.dataForm.status = data.msgTemplate.status
this.dataForm.name = data.msgTemplate.name
}
})
}
})
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/manage/msgtemplate/${!this.dataForm.templateId ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'templateId': this.dataForm.templateId || undefined,
'title': this.dataForm.title,
'data': this.dataForm.data,
'url': this.dataForm.url,
'color': this.dataForm.color,
'status': this.dataForm.status,
'name': this.dataForm.name
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<template>
<div class="mod-config">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button v-if="isAuth('wx:msgtemplate:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('wx:msgtemplate:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="templateId"
header-align="center"
align="center"
show-overflow-tooltip
label="ID">
</el-table-column>
<el-table-column
prop="title"
header-align="center"
align="center"
label="标题">
<a :href="scope.row.url" slot-scope="scope">{{scope.row.title}}</a>
</el-table-column>
<el-table-column
prop="data"
header-align="center"
align="center"
show-overflow-tooltip
label="内容">
</el-table-column>
<el-table-column
prop="status"
header-align="center"
align="center"
label="是否有效">
</el-table-column>
<el-table-column
prop="name"
header-align="center"
align="center"
label="模版名称">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.templateId)">修改</el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.templateId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './msgtemplate-add-or-update'
export default {
data () {
return {
dataForm: {
key: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/manage/msgtemplate/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'key': this.dataForm.key
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.templateId
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/msgtemplate/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
})
}
}
}
</script>
<template>
<div class="mod-config">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button v-if="isAuth('wx:templatemsglog:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="logId"
header-align="center"
align="center"
label="ID">
</el-table-column>
<el-table-column
prop="touser"
header-align="center"
align="center"
label="openid">
</el-table-column>
<el-table-column
prop="templateId"
header-align="center"
align="center"
show-overflow-tooltip
label="模板ID">
</el-table-column>
<el-table-column
prop="data"
header-align="center"
align="center"
show-overflow-tooltip
label="内容">
</el-table-column>
<el-table-column
prop="sendTime"
header-align="center"
align="center"
width="100"
label="发送时间">
</el-table-column>
<el-table-column
prop="sendResult"
header-align="center"
align="center"
show-overflow-tooltip
label="发送结果">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteHandle(scope.row.logId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</div>
</template>
<script>
export default {
data () {
return {
dataForm: {
key: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/manage/templatemsglog/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'key': this.dataForm.key,
'sidx':'send_time',
'order':'desc'
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount= data.page.totalCount
} else {
this.dataList = []
this.totalCount= 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.logId
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/templatemsglog/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
})
}
}
}
</script>
<template>
<div>
<div class="menu-input-group" style="border-bottom: 2px #e8e8e8 solid;">
<div class="menu-name">{{button.name}}</div>
<div class="menu-del" @click="$emit('delMenu')">删除菜单</div>
</div>
<div class="menu-input-group">
<div class="menu-label">菜单名称</div>
<div class="menu-input">
<input type="text" name="name" placeholder="请输入菜单名称" class="menu-input-text" v-model="button.name" @input="checkMenuName(button.name)">
<p class="menu-tips" style="color:#e15f63" v-show="menuNameBounds">字数超过上限</p>
<p class="menu-tips">字数不超过{{selectedMenuLevel==1?'5':'8'}}个汉字</p>
</div>
</div>
<block v-show="!button.subButtons || button.subButtons.length==0">
<div class="menu-input-group">
<div class="menu-label">菜单内容</div>
<div class="menu-input">
<select v-model="button.type" name="type" class="menu-input-text">
<option value="view">跳转网页(view)</option>
<option value="mediaId">发送消息(mediaId)</option>
<!--<option value="view_limited">跳转公众号图文消息链接(view_limited)</option>-->
<option value="miniprogram">打开指定小程序(miniprogram)</option>
<option value="click">自定义点击事件(click)</option>
<option value="scancode_push">扫码上传消息(scancode_push)</option>
<option value="scancode_waitmsg">扫码提示下发(scancode_waitmsg)</option>
<option value="pic_sysphoto">系统相机拍照(pic_sysphoto)</option>
<option value="pic_photo_or_album">弹出拍照或者相册(pic_photo_or_album)</option>
<option value="pic_weixin">弹出微信相册(pic_weixin)</option>
<option value="location_select">弹出地理位置选择器(location_select)</option>
</select>
</div>
</div>
<div class="menu-content" v-if="button.type=='view'">
<div class="menu-input-group">
<p class="menu-tips">订阅者点击该子菜单会跳到以下链接</p>
<div class="menu-label">页面地址</div>
<div class="menu-input">
<input type="text" placeholder="" class="menu-input-text" v-model="button.url">
</div>
</div>
</div>
<div class="menu-content" v-else-if="button.type=='mediaId'">
<div class="menu-input-group">
<p class="menu-tips">订阅者点击该菜单会收到以下图文消息</p>
<div class="menu-label">media_id</div>
<div class="menu-input">
<input type="text" placeholder="图文消息mediaId" class="menu-input-text" v-model="button.mediaId">
</div>
</div>
</div>
<div class="menu-content" v-else-if="button.type=='miniprogram'">
<div class="menu-input-group">
<p class="menu-tips">订阅者点击该子菜单会跳到以下小程序</p>
<div class="menu-label">小程序appId</div>
<div class="menu-input">
<input type="text" placeholder="小程序的appId(仅认证公众号可配置)" class="menu-input-text" v-model="button.appId">
</div>
</div>
<div class="menu-input-group">
<div class="menu-label">小程序路径</div>
<div class="menu-input">
<input type="text" placeholder="小程序的页面路径 pages/index/index" class="menu-input-text" v-model="button.pagePath">
</div>
</div>
<div class="menu-input-group">
<div class="menu-label">备用网页</div>
<div class="menu-input">
<input type="text" placeholder="" class="menu-input-text" v-model="button.url">
<p class="menu-tips">旧版微信客户端无法支持小程序,用户点击菜单时将会打开备用网页。</p>
</div>
</div>
</div>
<div class="menu-content" v-else>
<div class="menu-input-group">
<p class="menu-tips">用于消息接口推送,不超过128字节</p>
<div class="menu-label">菜单KEY值</div>
<div class="menu-input">
<input type="text" placeholder="" class="menu-input-text" v-model="button.key">
</div>
</div>
</div>
</block>
</div>
</template>
<script>
export default {
props:{
selectedMenuLevel:{
type:Number,
default:1
},
button:{
type:Object,
required:true
}
},
data(){
return{
menuNameBounds:false,//菜单长度是否过长
}
},
methods:{
//检查菜单名称长度
checkMenuName:function(val){
if(this.selectedMenuLevel==1&&this.getMenuNameLen(val)<=10){
this.menuNameBounds=false
}else if(this.selectedMenuLevel==2&&this.getMenuNameLen(val)<=16){
this.menuNameBounds=false
}else{
this.menuNameBounds=true
}
},
//获取菜单名称长度
getMenuNameLen: function (val) {
var len = 0;
for (var i = 0; i < val.length; i++) {
var a = val.charAt(i);
a.match(/[^\x00-\xff]/ig) != null?len += 2:len += 1;
}
return len;
}
}
}
</script>
\ No newline at end of file
<template>
<div>
<div id="app-menu">
<!-- 预览窗 -->
<div class="weixin-preview">
<div class="weixin-bd">
<ul class="weixin-menu" id="weixin-menu" >
<li v-for="(btn,i) in menu.buttons" :key="i" class="menu-item" :class="{current:selectedMenuIndex===i&&selectedMenuLevel==1}" @click="selectMenu(i)">
<div class="menu-item-title">
<span>{{ btn.name }}</span>
</div>
<ul class="weixin-sub-menu" v-show="selectedMenuIndex===i">
<li v-for="(sub,i2) in btn.subButtons" :key="i2" class="menu-sub-item" :class="{current:selectedSubMenuIndex===i2&&selectedMenuLevel==2}" @click.stop="selectSubMenu(i2)">
<div class="menu-item-title">
<span>{{sub.name}}</span>
</div>
</li>
<li v-if="btn.subButtons.length<5" class="menu-sub-item" @click.stop="addMenu(2)">
<div class="menu-item-title">
<i class="el-icon-plus"></i>
</div>
</li>
<i class="menu-arrow arrow_out"></i>
<i class="menu-arrow arrow_in"></i>
</ul>
</li>
<li class="menu-item" v-if="menu.buttons.length<3" @click="addMenu(1)"> <i class="el-icon-plus"></i></li>
</ul>
</div>
</div>
<!-- 菜单编辑器 -->
<div class="weixin-menu-detail" v-if="selectedMenuLevel>0">
<wx-menu-button-editor :button="selectedButton" :selectedMenuLevel="selectedMenuLevel" @delMenu="delMenu"></wx-menu-button-editor>
</div>
</div>
<div class="weixin-btn-group" v-if="isAuth('wx:menu:save')" @click="updateWxMenu">
<el-button type="success" icon="el-icon-upload" >发布</el-button>
<el-button type="warning" icon="el-icon-delete" @click="delMenu">清空</el-button>
</div>
</div>
</template>
<script>
export default {
components:{
wxMenuButtonEditor:()=>import('./wx-menu-button-editor')
},
data() {
return {
menu: {'buttons': []},//当前菜单
selectedMenuIndex:'',//当前选中菜单索引
selectedSubMenuIndex:'',//当前选中子菜单索引
selectedMenuLevel:0,//选中菜单级别
selectedButton:''//选中的菜单按钮
}
},
mounted() {
this.getWxMenu();
},
methods: {
getWxMenu() {
this.$http({
url:this.$http.adornUrl('/manage/wxMenu/getMenu')
}).then(({data}) => {
if (data.code == 200) {
this.menu = data.data.menu;
} else {
this.$message({
type: 'error',
message: data.msg
});
}
});
},
//选中主菜单
selectMenu(i){
this.selectedMenuLevel=1
this.selectedSubMenuIndex=''
this.selectedMenuIndex=i
this.selectedButton=this.menu.buttons[i]
},
//选中子菜单
selectSubMenu(i){
this.selectedMenuLevel=2
this.selectedSubMenuIndex=i
this.selectedButton=this.menu.buttons[this.selectedMenuIndex].subButtons[i]
},
//添加菜单
addMenu(level){
if(level==1&&this.menu.buttons.length<3){
this.menu.buttons.push({
"type": "view",
"name": "菜单名称",
"subButtons": [],
"url":""
})
this.selectMenu(this.menu.buttons.length-1)
}
if(level==2&&this.menu.buttons[this.selectedMenuIndex].subButtons.length<5){
this.menu.buttons[this.selectedMenuIndex].subButtons.push({
"type": "view",
"name": "子菜单名称",
"url":""
})
this.selectSubMenu(this.menu.buttons[this.selectedMenuIndex].subButtons.length-1)
}
},
//删除菜单
delMenu(){
if(this.selectedMenuLevel==1&&confirm('删除后菜单下设置的内容将被删除')){
this.menu.buttons.splice(this.selectedMenuIndex, 1);
this.selectedMenuLevel=0//删除主菜单后不选中任何菜单
this.selectedMenuIndex=''
this.selectedSubMenuIndex=''
this.selectedButton=''
}else if(this.selectedMenuLevel==2){
this.menu.buttons[this.selectedMenuIndex].subButtons.splice(this.selectedSubMenuIndex, 1);
this.selectedMenuLevel=1//删除后选中主菜单不选子菜单
this.selectedSubMenuIndex = '';
this.selectedButton=this.menu.buttons[this.selectedMenuIndex]
}
},
updateWxMenu(){
this.$http({
url:this.$http.adornUrl('/manage/wxMenu/updateMenu'),
data:this.menu,
method:'post'
}).then(({data}) => {
if (data.code == 200) {
this.$message({
message: '操作成功',
type: 'success'
})
} else {
this.$message({
type: 'error',
message: data.msg
});
}
});
}
}
}
</script>
<style src="@/assets/css/wx-menu.css"></style
<style scoped>
@import '@/assets/css/wx-menu.css';
.form_contianer {
padding: 25px;
border-radius: 5px;
text-align: center;
background-color: #fff;
}
.form_contianer .submit_btn {
width: 100%;
font-size: 16px;
}
</style>
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="100px">
<el-form-item label="二维码类型" prop="isTemp">
<el-radio v-model="dataForm.isTemp" :label="true">临时</el-radio>
<el-radio v-model="dataForm.isTemp" :label="false">永久</el-radio>
<div>
<a class="text-warning" v-show="!dataForm.isTemp" target="_blank" href="https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html">注意永久二维码上限10万个,且暂时无法删除旧的二维码</a>
</div>
</el-form-item>
<el-form-item label="场景值" prop="sceneStr">
<el-input v-model="dataForm.sceneStr" placeholder="任意字符串" maxlength="64"></el-input>
</el-form-item>
<el-form-item label="失效时间/秒" prop="expireSeconds" v-if="dataForm.isTemp">
<el-input v-model="dataForm.expireSeconds" placeholder="单位:秒,最大2592000(30天)"></el-input>
<div>最大30天,当前设置:<span class="text-warning">{{dataForm.expireSeconds/(24*3600)}}</span></div>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data () {
return {
visible: false,
dataForm: {
isTemp: true,
sceneStr: '',
expireSeconds: 2592000
},
dataRule: {
isTemp: [
{ required: true, message: '二维码类型不能为空', trigger: 'blur' }
],
sceneStr: [
{ required: true, message: '场景值ID不能为空', trigger: 'blur' }
],
expireSeconds: [
{ required: true, message: '该二维码失效时间不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init (id) {
this.dataForm.id = id || 0
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
},
// 表单提交
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/manage/wxQrCode/createTicket`),
method: 'post',
data: this.$http.adornData({
'isTemp': this.dataForm.isTemp,
'sceneStr': this.dataForm.sceneStr,
'expireSeconds': this.dataForm.expireSeconds
})
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<template>
<div class="mod-config">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.sceneStr" placeholder="场景值" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button v-if="isAuth('wx:wxqrcode:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('wx:wxqrcode:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="id"
header-align="center"
align="center"
label="ID">
</el-table-column>
<el-table-column
prop="isTemp"
header-align="center"
align="center"
label="类型">
<span slot-scope="scope">{{scope.row.isTemp?'临时':'永久'}}</span>
</el-table-column>
<el-table-column
prop="sceneStr"
header-align="center"
align="center"
label="场景值">
</el-table-column>
<el-table-column
prop="ticket"
header-align="center"
align="center"
show-overflow-tooltip
label="二维码图片">
<a :href="'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket='+scope.row.ticket" slot-scope="scope">{{scope.row.ticket}}</a>
</el-table-column>
<el-table-column
prop="url"
header-align="center"
align="center"
show-overflow-tooltip
label="解析后的地址">
<a :href="scope.row.url" slot-scope="scope">{{scope.row.url}}</a>
</el-table-column>
<el-table-column
prop="expireTime"
header-align="center"
align="center"
width="100"
label="失效时间">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteHandle(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './wxqrcode-add-or-update'
export default {
data () {
return {
dataForm: {
sceneStr: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalPage: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated () {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/manage/wxQrCode/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'sceneStr': this.dataForm.sceneStr,
'sidx':'id',
'order':'desc'
})
}).then(({data}) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalPage = data.page.totalCount
} else {
this.dataList = []
this.totalPage = 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.id
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?(仅删存档)`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/wxQrCode/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
})
}
}
}
</script>
module.exports = {
publicPath: "./",
devServer: {
proxy: {
'/wx': {
target: 'http://localhost:8088/'
}
},
port:8001,
inline:false //实时编译
},
configureWebpack:{
devServer: {
disableHostCheck: true
},
externals: {
vue: "Vue",
"vue-router": "Router",
"element-ui": "ELEMENT",
"tinymce":"tinymce"
}
},
chainWebpack: config => {
// 移除 prefetch 插件
config.plugins.delete('prefetch')
},
outputDir: undefined,
assetsDir: undefined,
runtimeCompiler: undefined,
productionSourceMap: false,
parallel: undefined,
css: undefined
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment