Commit 7a52d6a811ee17211130eb870d421cb90a4c76b0

Authored by monkeyhouyi
1 parent 15ee21cf

1

components/uni-forms-item/uni-forms-item.vue 0 → 100644
  1 +<template>
  2 + <view class="uni-forms-item" :class="{'uni-forms-item--border':border,'is-first-border':border&&isFirstBorder,'uni-forms-item-error':msg}">
  3 + <view class="uni-forms-item__inner" :class="['is-direction-'+labelPos,]">
  4 + <view v-if="label" class="uni-forms-item__label" :style="{width:labelWid+'px',justifyContent: justifyContent}">
  5 + <slot name="left">
  6 + <uni-icons v-if="leftIcon" class="label-icon" size="16" :type="leftIcon" :color="iconColor" />
  7 + <text>{{label}}</text>
  8 + <text v-if="required" class="is-required">*</text>
  9 + </slot>
  10 + </view>
  11 + <view class="uni-forms-item__content" :class="{'is-input-error-border': msg}">
  12 + <slot></slot>
  13 + </view>
  14 + </view>
  15 + <view class="uni-error-message" :class="{'uni-error-msg--boeder':border}" :style="{
  16 + paddingLeft: (labelPos === 'left'? Number(labelWid)+5:5) + 'px'
  17 + }">{{ showMsg === 'undertext' ? msg:'' }}</view>
  18 + </view>
  19 +</template>
  20 +
  21 +<script>
  22 + /**
  23 + * Field 输入框
  24 + * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
  25 + * @tutorial https://ext.dcloud.net.cn/plugin?id=21001
  26 + * @property {Boolean} required 是否必填,左边显示红色"*"号(默认false)
  27 + * @property {String} validateTrigger = [bind|submit] 校验触发器方式 默认 submit 可选
  28 + * @value bind 发生变化时触发
  29 + * @value submit 提交时触发
  30 + * @property {String } leftIcon label左边的图标,限 uni-ui 的图标名称
  31 + * @property {String } iconColor 左边通过icon配置的图标的颜色(默认#606266)
  32 + * @property {String } label 输入框左边的文字提示
  33 + * @property {Number } labelWidth label的宽度,单位px(默认65)
  34 + * @property {String } labelAlign = [left|center|right] label的文字对齐方式(默认left)
  35 + * @value left label 左侧显示
  36 + * @value center label 居中
  37 + * @value right label 右侧对齐
  38 + * @property {String } labelPosition = [top|left] label的文字的位置(默认left)
  39 + * @value top 顶部显示 label
  40 + * @value left 左侧显示 label
  41 + * @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
  42 + * @property {String } name 表单域的属性名,在使用校验规则时必填
  43 + */
  44 +
  45 +
  46 +
  47 + export default {
  48 + name: "uniFormsItem",
  49 + props: {
  50 + // 自定义内容
  51 + custom: {
  52 + type: Boolean,
  53 + default: false
  54 + },
  55 + // 是否显示报错信息
  56 + showMessage: {
  57 + type: Boolean,
  58 + default: true
  59 + },
  60 + name: String,
  61 + required: Boolean,
  62 + validateTrigger: {
  63 + type: String,
  64 + default: ''
  65 + },
  66 + leftIcon: String,
  67 + iconColor: {
  68 + type: String,
  69 + default: '#606266'
  70 + },
  71 + label: String,
  72 + // 左边标题的宽度单位px
  73 + labelWidth: {
  74 + type: [Number, String],
  75 + default: ''
  76 + },
  77 + // 对齐方式,left|center|right
  78 + labelAlign: {
  79 + type: String,
  80 + default: ''
  81 + },
  82 + // lable的位置,可选为 left-左边,top-上边
  83 + labelPosition: {
  84 + type: String,
  85 + default: ''
  86 + },
  87 + errorMessage: {
  88 + type: [String, Boolean],
  89 + default: ''
  90 + }
  91 + },
  92 + data() {
  93 + return {
  94 + errorTop: false,
  95 + errorBottom: false,
  96 + labelMarginBottom: '',
  97 + errorWidth: '',
  98 + errMsg: '',
  99 + val: '',
  100 + labelPos: '',
  101 + labelWid: '',
  102 + labelAli: '',
  103 + showMsg: 'undertext',
  104 + border: false,
  105 + isFirstBorder: false
  106 + };
  107 + },
  108 + computed: {
  109 + msg() {
  110 + return this.errorMessage || this.errMsg;
  111 + },
  112 + fieldStyle() {
  113 + let style = {}
  114 + if (this.labelPos == 'top') {
  115 + style.padding = '0 0'
  116 + this.labelMarginBottom = '6px'
  117 + }
  118 + if (this.labelPos == 'left' && this.msg !== false && this.msg != '') {
  119 + style.paddingBottom = '0px'
  120 + this.errorBottom = true
  121 + this.errorTop = false
  122 + } else if (this.labelPos == 'top' && this.msg !== false && this.msg != '') {
  123 + this.errorBottom = false
  124 + this.errorTop = true
  125 + } else {
  126 + // style.paddingBottom = ''
  127 + this.errorTop = false
  128 + this.errorBottom = false
  129 + }
  130 + return style
  131 + },
  132 +
  133 + // uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法
  134 + justifyContent() {
  135 + if (this.labelAli === 'left') return 'flex-start';
  136 + if (this.labelAli === 'center') return 'center';
  137 + if (this.labelAli === 'right') return 'flex-end';
  138 + }
  139 + },
  140 + watch: {
  141 + validateTrigger(trigger) {
  142 + this.formTrigger = trigger
  143 + }
  144 + },
  145 + created() {
  146 + this.form = this.getForm()
  147 + this.group = this.getForm('uniGroup')
  148 + this.formRules = []
  149 + this.formTrigger = this.validateTrigger
  150 + if (this.form) {
  151 + this.form.childrens.push(this)
  152 + }
  153 + this.init()
  154 + },
  155 + destroyed() {
  156 + if (this.form) {
  157 + this.form.childrens.forEach((item, index) => {
  158 + if (item === this) {
  159 + this.form.childrens.splice(index, 1)
  160 + delete this.form.formData[item.name]
  161 + }
  162 + })
  163 + }
  164 + },
  165 + methods: {
  166 + init() {
  167 + if (this.form) {
  168 + let {
  169 + formRules,
  170 + validator,
  171 + formData,
  172 + value,
  173 + labelPosition,
  174 + labelWidth,
  175 + labelAlign,
  176 + errShowType
  177 + } = this.form
  178 +
  179 + this.labelPos = this.labelPosition ? this.labelPosition : labelPosition
  180 + this.labelWid = this.label ? (this.labelWidth ? this.labelWidth : labelWidth):0
  181 + this.labelAli = this.labelAlign ? this.labelAlign : labelAlign
  182 +
  183 + // 判断第一个 item
  184 + if (!this.form.isFirstBorder) {
  185 + this.form.isFirstBorder = true
  186 + this.isFirstBorder = true
  187 + }
  188 +
  189 + // 判断 group 里的第一个 item
  190 + if (this.group) {
  191 + if (!this.group.isFirstBorder) {
  192 + this.group.isFirstBorder = true
  193 + this.isFirstBorder = true
  194 + }
  195 + }
  196 +
  197 + this.border = this.form.border
  198 + this.showMsg = errShowType
  199 +
  200 + if (formRules) {
  201 + this.formRules = formRules[this.name] || {}
  202 + }
  203 +
  204 + this.validator = validator
  205 + } else {
  206 + this.labelPos = this.labelPosition || 'left'
  207 + this.labelWid = this.labelWidth || 65
  208 + this.labelAli = this.labelAlign || 'left'
  209 + }
  210 + },
  211 + /**
  212 + * 获取父元素实例
  213 + */
  214 + getForm(name = 'uniForms') {
  215 + let parent = this.$parent;
  216 + let parentName = parent.$options.name;
  217 + while (parentName !== name) {
  218 + parent = parent.$parent;
  219 + if (!parent) return false
  220 + parentName = parent.$options.name;
  221 + }
  222 + return parent;
  223 + },
  224 +
  225 + /**
  226 + * 移除该表单项的校验结果
  227 + */
  228 + clearValidate() {
  229 + this.errMsg = ''
  230 + },
  231 +
  232 + setValue(value){
  233 + if (this.name) {
  234 + if(this.errMsg) this.errMsg = ''
  235 + this.form.formData[this.name] = this.form._getValue(this.name, value)
  236 + if(!this.formRules || (typeof(this.formRules) && JSON.stringify(this.formRules) === '{}')) return
  237 + this.triggerCheck(this.form._getValue(this.name, value))
  238 + }
  239 + },
  240 +
  241 + /**
  242 + * 校验规则
  243 + * @param {Object} value
  244 + */
  245 + async triggerCheck(value, callback) {
  246 + let promise = null;
  247 + this.errMsg = ''
  248 + // if no callback, return promise
  249 + // if (callback && typeof callback !== 'function' && Promise) {
  250 + // promise = new Promise((resolve, reject) => {
  251 + // callback = function(valid) {
  252 + // !valid ? resolve(valid) : reject(valid)
  253 + // };
  254 + // });
  255 + // }
  256 + // if (!this.validator) {
  257 + // typeof callback === 'function' && callback(null);
  258 + // if (promise) return promise
  259 + // }
  260 + if (!this.validator) return
  261 + const isNoField = this.isRequired(this.formRules.rules || [])
  262 + let isTrigger = this.isTrigger(this.formRules.validateTrigger, this.validateTrigger, this.form.validateTrigger)
  263 + let result = null
  264 + if (!(!isTrigger)) {
  265 + result = await this.validator.validateUpdate({
  266 + [this.name]: value
  267 + }, this.form.formData)
  268 + }
  269 + // 判断是否必填
  270 + if (!isNoField && !value) {
  271 + result = null
  272 + }
  273 + if (isTrigger && result && result.errorMessage) {
  274 + const inputComp = this.form.inputChildrens.find(child => child.rename === this.name)
  275 + if (inputComp) {
  276 + inputComp.errMsg = result.errorMessage
  277 + }
  278 + if (this.form.errShowType === 'toast') {
  279 + uni.showToast({
  280 + title: result.errorMessage || '校验错误',
  281 + icon: 'none'
  282 + })
  283 + }
  284 + if (this.form.errShowType === 'modal') {
  285 + uni.showModal({
  286 + title: '提示',
  287 + content: result.errorMessage || '校验错误'
  288 + })
  289 + }
  290 + }
  291 +
  292 + this.errMsg = !result ? '' : result.errorMessage
  293 + // 触发validate事件
  294 + this.form.validateCheck(result ? result : null)
  295 + // typeof callback === 'function' && callback(result ? result : null);
  296 + // if (promise) return promise
  297 +
  298 + },
  299 + /**
  300 + * 触发时机
  301 + * @param {Object} event
  302 + */
  303 + isTrigger(rule, itemRlue, parentRule) {
  304 + let rl = true;
  305 + // bind submit
  306 + if (rule === 'submit' || !rule) {
  307 + if (rule === undefined) {
  308 + if (itemRlue !== 'bind') {
  309 + if (!itemRlue) {
  310 + return parentRule === 'bind' ? true : false
  311 + }
  312 + return false
  313 + }
  314 + return true
  315 + }
  316 + return false
  317 + }
  318 + return true;
  319 + },
  320 + // 是否有必填字段
  321 + isRequired(rules) {
  322 + let isNoField = false
  323 + for (let i = 0; i < rules.length; i++) {
  324 + const ruleData = rules[i]
  325 + if (ruleData.required) {
  326 + isNoField = true
  327 + break
  328 + }
  329 + }
  330 + return isNoField
  331 + }
  332 + }
  333 + };
  334 +</script>
  335 +
  336 +<style lang="scss" scoped>
  337 + .uni-forms-item {
  338 + position: relative;
  339 + // padding: 16px 14px;
  340 + text-align: left;
  341 + color: #333;
  342 + font-size: 14px;
  343 + margin-bottom: 22px;
  344 + background-color: #fff;
  345 + }
  346 +
  347 + .uni-forms-item__inner {
  348 + /* #ifndef APP-NVUE */
  349 + display: flex;
  350 + /* #endif */
  351 + // flex-direction: row;
  352 + // align-items: center;
  353 + }
  354 +
  355 + .is-direction-left {
  356 + flex-direction: row;
  357 + }
  358 +
  359 + .is-direction-top {
  360 + flex-direction: column;
  361 + }
  362 +
  363 + .uni-forms-item__label {
  364 + /* #ifndef APP-NVUE */
  365 + display: flex;
  366 + flex-shrink: 0;
  367 + /* #endif */
  368 + flex-direction: row;
  369 + align-items: center;
  370 + font-size: 14px;
  371 + color: #333;
  372 + width: 65px;
  373 + // line-height: 2;
  374 + // margin-top: 3px;
  375 + padding: 5px 0;
  376 + box-sizing: border-box;
  377 + height: 36px;
  378 + margin-right: 5px;
  379 + }
  380 +
  381 + .uni-forms-item__content {
  382 + /* #ifndef APP-NVUE */
  383 + width: 100%;
  384 + // display: flex;
  385 + /* #endif */
  386 + // flex: 1;
  387 + // flex-direction: row;
  388 + // align-items: center;
  389 + box-sizing: border-box;
  390 + min-height: 36px;
  391 + }
  392 +
  393 +
  394 + .label-icon {
  395 + margin-right: 5px;
  396 + margin-top: -1px;
  397 + }
  398 +
  399 + // 必填
  400 + .is-required {
  401 + color: $uni-color-error;
  402 + }
  403 +
  404 + .uni-error-message {
  405 + position: absolute;
  406 + bottom: -17px;
  407 + left: 0;
  408 + line-height: 12px;
  409 + color: $uni-color-error;
  410 + font-size: 12px;
  411 + text-align: left;
  412 + }
  413 +
  414 + .uni-error-msg--boeder {
  415 + position: relative;
  416 + bottom: 0;
  417 + line-height: 22px;
  418 + }
  419 +
  420 + .is-input-error-border {
  421 + border-color: $uni-color-error;
  422 + }
  423 +
  424 + .uni-forms-item--border {
  425 + margin-bottom: 0;
  426 + padding: 10px 15px;
  427 + // padding-bottom: 0;
  428 + border-top: 1px #eee solid;
  429 + }
  430 +
  431 + .uni-forms-item-error {
  432 + padding-bottom: 0;
  433 + }
  434 +
  435 + .is-first-border {
  436 + border: none;
  437 + }
  438 +</style>
components/uni-forms/uni-forms.vue 0 → 100644
  1 +<template>
  2 + <!-- -->
  3 + <view class="uni-forms" :class="{'uni-forms--top':!border}">
  4 + <form @submit.stop="submitForm" @reset="resetForm">
  5 + <slot></slot>
  6 + </form>
  7 + </view>
  8 +</template>
  9 +
  10 +<script>
  11 + /**
  12 + * Forms 表单
  13 + * @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据
  14 + * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
  15 + * @property {Object} rules 表单校验规则
  16 + * @property {String} validateTrigger = [bind|submit] 校验触发器方式 默认 submit 可选
  17 + * @value bind 发生变化时触发
  18 + * @value submit 提交时触发
  19 + * @property {String} labelPosition = [top|left] label 位置 默认 left 可选
  20 + * @value top 顶部显示 label
  21 + * @value left 左侧显示 label
  22 + * @property {String} labelWidth label 宽度,默认 65px
  23 + * @property {String} labelAlign = [left|center|right] label 居中方式 默认 left 可选
  24 + * @value left label 左侧显示
  25 + * @value center label 居中
  26 + * @value right label 右侧对齐
  27 + * @property {String} errShowType = [undertext|toast|modal] 校验错误信息提示方式
  28 + * @value undertext 错误信息在底部显示
  29 + * @value toast 错误信息toast显示
  30 + * @value modal 错误信息modal显示
  31 + * @event {Function} submit 提交时触发
  32 + */
  33 + import Vue from 'vue'
  34 + Vue.prototype.binddata = function(name, value, formName) {
  35 + if (formName) {
  36 + this.$refs[formName].setValue(name, value)
  37 + } else {
  38 + let formVm
  39 + for (let i in this.$refs) {
  40 + const vm = this.$refs[i]
  41 + if (vm && vm.$options && vm.$options.name === 'uniForms') {
  42 + formVm = vm
  43 + break
  44 + }
  45 + }
  46 + if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性')
  47 + formVm.setValue(name, value)
  48 + }
  49 + }
  50 +
  51 + import Validator from './validate.js'
  52 +
  53 + export default {
  54 + name: 'uniForms',
  55 + props: {
  56 + value: {
  57 + type: Object,
  58 + default () {
  59 + return {}
  60 + }
  61 + },
  62 + // 表单校验规则
  63 + rules: {
  64 + type: Object,
  65 + default () {
  66 + return {}
  67 + }
  68 + },
  69 + // 校验触发器方式,默认 关闭
  70 + validateTrigger: {
  71 + type: String,
  72 + default: ''
  73 + },
  74 + // label 位置,可选值 top/left
  75 + labelPosition: {
  76 + type: String,
  77 + default: 'left'
  78 + },
  79 + // label 宽度,单位 px
  80 + labelWidth: {
  81 + type: [String, Number],
  82 + default: 65
  83 + },
  84 + // label 居中方式,可选值 left/center/right
  85 + labelAlign: {
  86 + type: String,
  87 + default: 'left'
  88 + },
  89 + errShowType: {
  90 + type: String,
  91 + default: 'undertext'
  92 + },
  93 + border: {
  94 + type: Boolean,
  95 + default: false
  96 + }
  97 + },
  98 + data() {
  99 + return {
  100 + formData: {}
  101 + };
  102 + },
  103 + watch: {
  104 + rules(newVal) {
  105 + this.init(newVal)
  106 + },
  107 + trigger(trigger) {
  108 + this.formTrigger = trigger
  109 + },
  110 + },
  111 + created() {
  112 + let _this = this
  113 + this.childrens = []
  114 + this.inputChildrens = []
  115 + this.checkboxChildrens = []
  116 + this.formRules = []
  117 + // this.init(this.rules)
  118 + },
  119 + mounted() {
  120 + this.init(this.rules)
  121 + },
  122 + methods: {
  123 + init(formRules) {
  124 + // 判断是否有规则
  125 + if (Object.keys(formRules).length > 0) {
  126 + this.formTrigger = this.trigger
  127 + this.formRules = formRules
  128 + if (!this.validator) {
  129 + this.validator = new Validator(formRules)
  130 + }
  131 + } else {
  132 + return
  133 + }
  134 + // 判断表单存在那些实例
  135 + for (let i in this.value) {
  136 + const itemData = this.childrens.find(v => v.name === i)
  137 + if (itemData) {
  138 + this.formData[i] = this.value[i]
  139 + itemData.init()
  140 + }
  141 + }
  142 +
  143 + // watch 每个属性 ,需要知道具体那个属性发变化
  144 + Object.keys(this.value).forEach((key) => {
  145 + this.$watch('value.' + key, (newVal) => {
  146 + const itemData = this.childrens.find(v => v.name === key)
  147 + if (itemData) {
  148 + this.formData[key] = this._getValue(key, newVal)
  149 + itemData.init()
  150 + } else {
  151 + this.formData[key] = this.value[key] || null
  152 + }
  153 + })
  154 + })
  155 + },
  156 + /**
  157 + * 设置校验规则
  158 + * @param {Object} formRules
  159 + */
  160 + setRules(formRules) {
  161 + this.init(formRules)
  162 + },
  163 + /**
  164 + * 公开给用户使用
  165 + * 设置自定义表单组件 value 值
  166 + * @param {String} name 字段名称
  167 + * @param {String} value 字段值
  168 + */
  169 + setValue(name, value, callback) {
  170 + let example = this.childrens.find(child => child.name === name)
  171 + if (!example) return null
  172 + value = this._getValue(example.name, value)
  173 + this.formData[name] = value
  174 + example.val = value
  175 + this.$emit('input', Object.assign({}, this.value, this.formData))
  176 + return example.triggerCheck(value, callback)
  177 + },
  178 +
  179 + /**
  180 + * TODO 表单提交, 小程序暂不支持这种用法
  181 + * @param {Object} event
  182 + */
  183 + submitForm(event) {
  184 + const value = event.detail.value
  185 + return this.validateAll(value || this.formData, 'submit')
  186 + },
  187 +
  188 + /**
  189 + * 表单重置
  190 + * @param {Object} event
  191 + */
  192 + resetForm(event) {
  193 + this.childrens.forEach(item => {
  194 + item.errMsg = ''
  195 + const inputComp = this.inputChildrens.find(child => child.rename === item.name)
  196 + if (inputComp) {
  197 + inputComp.errMsg = ''
  198 + inputComp.$emit('input', inputComp.multiple ? [] : '')
  199 + }
  200 + })
  201 +
  202 + this.childrens.forEach((item) => {
  203 + if (item.name) {
  204 + this.formData[item.name] = this._getValue(item.name, '')
  205 + }
  206 + })
  207 +
  208 + this.$emit('input', this.formData)
  209 + this.$emit('reset', event)
  210 + },
  211 +
  212 + /**
  213 + * 触发表单校验,通过 @validate 获取
  214 + * @param {Object} validate
  215 + */
  216 + validateCheck(validate) {
  217 + if (validate === null) validate = null
  218 + this.$emit('validate', validate)
  219 + },
  220 + /**
  221 + * 校验所有或者部分表单
  222 + */
  223 + async validateAll(invalidFields, type, callback) {
  224 + this.childrens.forEach(item => {
  225 + item.errMsg = ''
  226 + })
  227 +
  228 + let promise;
  229 + if (!callback && typeof callback !== 'function' && Promise) {
  230 + promise = new Promise((resolve, reject) => {
  231 + callback = function(valid, invalidFields) {
  232 + !valid ? resolve(invalidFields) : reject(valid);
  233 + };
  234 + });
  235 + }
  236 +
  237 + let fieldsValue = {}
  238 + let tempInvalidFields = Object.assign({}, invalidFields)
  239 +
  240 + Object.keys(this.formRules).forEach(item => {
  241 + const values = this.formRules[item]
  242 + const rules = (values && values.rules) || []
  243 + let isNoField = false
  244 + for (let i = 0; i < rules.length; i++) {
  245 + const rule = rules[i]
  246 + if (rule.required) {
  247 + isNoField = true
  248 + break
  249 + }
  250 + }
  251 + // 如果存在 required 才会将内容插入校验对象
  252 + if (!isNoField && (!tempInvalidFields[item] && tempInvalidFields[item] !== false)) {
  253 + delete tempInvalidFields[item]
  254 + }
  255 + })
  256 +
  257 + // 循环字段是否存在于校验规则中
  258 + for (let i in this.formRules) {
  259 + for (let j in tempInvalidFields) {
  260 + if (i === j) {
  261 + fieldsValue[i] = tempInvalidFields[i]
  262 + }
  263 + }
  264 + }
  265 + let result = []
  266 + let example = null
  267 +
  268 + let newFormData = {}
  269 + this.childrens.forEach(v => {
  270 + newFormData[v.name] = this._getValue(v.name,invalidFields[v.name])
  271 + })
  272 + if (this.validator) {
  273 + for (let i in fieldsValue) {
  274 + // 循环校验,目的是异步校验
  275 + const resultData = await this.validator.validateUpdate({
  276 + [i]: fieldsValue[i]
  277 + }, this.formData)
  278 +
  279 + // 未通过
  280 + if (resultData) {
  281 + // 获取当前未通过子组件实例
  282 + example = this.childrens.find(child => child.name === resultData.key)
  283 + // 获取easyInput 组件实例
  284 + const inputComp = this.inputChildrens.find(child => child.rename === (example && example.name))
  285 + if (inputComp) {
  286 + inputComp.errMsg = resultData.errorMessage
  287 + }
  288 + result.push(resultData)
  289 + // 区分触发类型
  290 + if (this.errShowType === 'undertext') {
  291 + if (example) example.errMsg = resultData.errorMessage
  292 + } else {
  293 + if (this.errShowType === 'toast') {
  294 + uni.showToast({
  295 + title: resultData.errorMessage || '校验错误',
  296 + icon: 'none'
  297 + })
  298 + break
  299 + } else if (this.errShowType === 'modal') {
  300 + uni.showModal({
  301 + title: '提示',
  302 + content: resultData.errorMessage || '校验错误'
  303 + })
  304 + break
  305 + } else {
  306 + if (example) example.errMsg = resultData.errorMessage
  307 + }
  308 + }
  309 + }
  310 + }
  311 + }
  312 +
  313 + if (Array.isArray(result)) {
  314 + if (result.length === 0) result = null
  315 + }
  316 +
  317 + if (type === 'submit') {
  318 + this.$emit('submit', {
  319 + detail: {
  320 + value: newFormData,
  321 + errors: result
  322 + }
  323 + })
  324 + } else {
  325 + this.$emit('validate', result)
  326 + }
  327 +
  328 + callback && typeof callback === 'function' && callback(result, newFormData)
  329 +
  330 + if (promise && callback) {
  331 + return promise
  332 + } else {
  333 + return null
  334 + }
  335 + },
  336 +
  337 + /**
  338 + * 外部调用方法
  339 + * 手动提交校验表单
  340 + * 对整个表单进行校验的方法,参数为一个回调函数。
  341 + */
  342 + submit(callback) {
  343 + // Object.assign(this.formData,formData)
  344 + return this.validateAll(this.formData, 'submit', callback)
  345 + },
  346 +
  347 + /**
  348 + * 外部调用方法
  349 + * 校验表单
  350 + * 对整个表单进行校验的方法,参数为一个回调函数。
  351 + */
  352 + validate(callback) {
  353 + return this.validateAll(this.formData, '', callback)
  354 + },
  355 +
  356 + /**
  357 + * 部分表单校验
  358 + * @param {Object} props
  359 + * @param {Object} cb
  360 + */
  361 + validateField(props, callback) {
  362 + props = [].concat(props);
  363 + let invalidFields = {}
  364 + this.childrens.forEach(item => {
  365 + if (props.indexOf(item.name) !== -1) {
  366 + invalidFields = Object.assign({}, invalidFields, {
  367 + [item.name]: this.formData[item.name]
  368 + })
  369 + }
  370 + })
  371 + return this.validateAll(invalidFields, '', callback)
  372 + },
  373 +
  374 + /**
  375 + * 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
  376 + */
  377 + resetFields() {
  378 + this.resetForm()
  379 + },
  380 +
  381 + /**
  382 + * 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果
  383 + */
  384 + clearValidate(props) {
  385 + props = [].concat(props);
  386 + this.childrens.forEach(item => {
  387 + const inputComp = this.inputChildrens.find(child => child.rename === item.name)
  388 + if (props.length === 0) {
  389 + item.errMsg = ''
  390 + if (inputComp) {
  391 + inputComp.errMsg = ''
  392 + }
  393 + } else {
  394 + if (props.indexOf(item.name) !== -1) {
  395 + item.errMsg = ''
  396 + if (inputComp) {
  397 + inputComp.errMsg = ''
  398 + }
  399 + }
  400 + }
  401 + })
  402 + },
  403 + /**
  404 + * 把 value 转换成指定的类型
  405 + * @param {Object} key
  406 + * @param {Object} value
  407 + */
  408 + _getValue(key, value) {
  409 + const rules = (this.formRules[key] && this.formRules[key].rules) || []
  410 + const isRuleNum = rules.find(val => val.format && this.type_filter(val.format))
  411 + const isRuleBool = rules.find(val => val.format && val.format === 'boolean' || val.format === 'bool')
  412 + // 输入值为 number
  413 + if (isRuleNum) {
  414 + value = isNaN(value) ? value : (value === '' || value === null ? null : Number(value))
  415 + }
  416 + // 简单判断真假值
  417 + if (isRuleBool) {
  418 + value = !value ? false : true
  419 + }
  420 + return value
  421 + },
  422 + /**
  423 + * 过滤数字类型
  424 + * @param {Object} format
  425 + */
  426 + type_filter(format) {
  427 + return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp'
  428 + }
  429 + }
  430 + }
  431 +</script>
  432 +
  433 +<style lang="scss" scoped>
  434 + .uni-forms {
  435 + // overflow: hidden;
  436 + // padding: 10px 15px;
  437 + // background-color: #fff;
  438 + }
  439 +
  440 + .uni-forms--top {
  441 + padding: 10px 15px;
  442 + // padding-top: 22px;
  443 + }
  444 +</style>
components/uni-forms/validate.js 0 → 100644
  1 +
  2 +var pattern = {
  3 + email: /^\S+?@\S+?\.\S+?$/,
  4 + url: new RegExp("^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", 'i')
  5 +};
  6 +
  7 +const FORMAT_MAPPING = {
  8 + "int": 'number',
  9 + "bool": 'boolean',
  10 + "double": 'number',
  11 + "long": 'number',
  12 + "password": 'string'
  13 +}
  14 +
  15 +function formatMessage(args, resources) {
  16 + var defaultMessage = ['label']
  17 + defaultMessage.forEach((item) => {
  18 + if (args[item] === undefined) {
  19 + args[item] = ''
  20 + }
  21 + })
  22 +
  23 + let str = resources
  24 + for (let key in args) {
  25 + let reg = new RegExp('{' + key + '}')
  26 + str = str.replace(reg, args[key])
  27 + }
  28 + return str
  29 +}
  30 +
  31 +function isEmptyValue(value, type) {
  32 + if (value === undefined || value === null) {
  33 + return true;
  34 + }
  35 +
  36 + if (typeof value === 'string' && !value) {
  37 + return true;
  38 + }
  39 +
  40 + if (Array.isArray(value) && !value.length) {
  41 + return true;
  42 + }
  43 +
  44 + if (type === 'object' && !Object.keys(value).length) {
  45 + return true;
  46 + }
  47 +
  48 + return false;
  49 +}
  50 +
  51 +const types = {
  52 + integer(value) {
  53 + return types.number(value) && parseInt(value, 10) === value;
  54 + },
  55 + string(value) {
  56 + return typeof value === 'string';
  57 + },
  58 + number(value) {
  59 + if (isNaN(value)) {
  60 + return false;
  61 + }
  62 + return typeof value === 'number';
  63 + },
  64 + "boolean": function (value) {
  65 + return typeof value === 'boolean';
  66 + },
  67 + "float": function (value) {
  68 + return types.number(value) && !types.integer(value);
  69 + },
  70 + array(value) {
  71 + return Array.isArray(value);
  72 + },
  73 + object(value) {
  74 + return typeof value === 'object' && !types.array(value);
  75 + },
  76 + date(value) {
  77 + var v
  78 + if (value instanceof Date) {
  79 + v = value;
  80 + } else {
  81 + v = new Date(value);
  82 + }
  83 + return typeof v.getTime === 'function' && typeof v.getMonth === 'function' && typeof v.getYear === 'function' && !isNaN(v.getTime());
  84 + },
  85 + timestamp(value) {
  86 + if (!this.integer(value) || Math.abs(value).toString().length > 16) {
  87 + return false
  88 + }
  89 +
  90 + return this.date(value);
  91 + },
  92 + email(value) {
  93 + return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255;
  94 + },
  95 + url(value) {
  96 + return typeof value === 'string' && !!value.match(pattern.url);
  97 + },
  98 + pattern(reg, value) {
  99 + try {
  100 + return new RegExp(reg).test(value);
  101 + } catch (e) {
  102 + return false;
  103 + }
  104 + },
  105 + method(value) {
  106 + return typeof value === 'function';
  107 + }
  108 +}
  109 +
  110 +class RuleValidator {
  111 +
  112 + constructor(message) {
  113 + this._message = message
  114 + }
  115 +
  116 + async validateRule(key, value, data, allData) {
  117 + var result = null
  118 +
  119 + let rules = key.rules
  120 +
  121 + let hasRequired = rules.findIndex((item) => {
  122 + return item.required
  123 + })
  124 + if (hasRequired < 0) {
  125 + if (value === null || value === undefined) {
  126 + return result
  127 + }
  128 + if (typeof value === 'string' && !value.length) {
  129 + return result
  130 + }
  131 + }
  132 +
  133 + var message = this._message
  134 +
  135 + if (rules === undefined) {
  136 + return message['default']
  137 + }
  138 +
  139 + for (var i = 0; i < rules.length; i++) {
  140 + let rule = rules[i]
  141 + let vt = this._getValidateType(rule)
  142 +
  143 + if (key.label !== undefined) {
  144 + Object.assign(rule, {
  145 + label: key.label
  146 + })
  147 + }
  148 +
  149 + if (RuleValidatorHelper[vt]) {
  150 + result = RuleValidatorHelper[vt](rule, value, message)
  151 + if (result != null) {
  152 + break
  153 + }
  154 + }
  155 +
  156 + if (rule.validateExpr) {
  157 + let now = Date.now()
  158 + let resultExpr = rule.validateExpr(value, allData, now)
  159 + if (resultExpr === false) {
  160 + result = this._getMessage(rule, rule.errorMessage || this._message['default'])
  161 + break
  162 + }
  163 + }
  164 +
  165 + if (rule.validateFunction) {
  166 + result = await this.validateFunction(rule, value, data, allData, vt)
  167 + if (result !== null) {
  168 + break
  169 + }
  170 + }
  171 + }
  172 +
  173 + return result
  174 + }
  175 +
  176 + async validateFunction(rule, value, data, allData, vt) {
  177 + let result = null
  178 + try {
  179 + let callbackMessage = null
  180 + const res = await rule.validateFunction(rule, value, allData || data, (message) => {
  181 + callbackMessage = message
  182 + })
  183 + if (callbackMessage || (typeof res === 'string' && res) || res === false) {
  184 + result = this._getMessage(rule, callbackMessage || res, vt)
  185 + }
  186 + } catch (e) {
  187 + result = this._getMessage(rule, e.message, vt)
  188 + }
  189 + return result
  190 + }
  191 +
  192 + _getMessage(rule, message, vt) {
  193 + return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default'])
  194 + }
  195 +
  196 + _getValidateType(rule) {
  197 + // TODO
  198 + var result = ''
  199 + if (rule.required) {
  200 + result = 'required'
  201 + } else if (rule.format) {
  202 + result = 'format'
  203 + } else if (rule.range) {
  204 + result = 'range'
  205 + } else if (rule.maximum || rule.minimum) {
  206 + result = 'rangeNumber'
  207 + } else if (rule.maxLength || rule.minLength) {
  208 + result = 'rangeLength'
  209 + } else if (rule.pattern) {
  210 + result = 'pattern'
  211 + }
  212 + return result
  213 + }
  214 +}
  215 +
  216 +const RuleValidatorHelper = {
  217 + required(rule, value, message) {
  218 + if (rule.required && isEmptyValue(value, rule.format || typeof value)) {
  219 + return formatMessage(rule, rule.errorMessage || message.required);
  220 + }
  221 +
  222 + return null
  223 + },
  224 +
  225 + range(rule, value, message) {
  226 + const { range, errorMessage } = rule;
  227 +
  228 + let list = new Array(range.length);
  229 + for (let i = 0; i < range.length; i++) {
  230 + const item = range[i];
  231 + if (types.object(item) && item.value !== undefined) {
  232 + list[i] = item.value;
  233 + } else {
  234 + list[i] = item;
  235 + }
  236 + }
  237 +
  238 + let result = false
  239 + if (Array.isArray(value)) {
  240 + result = (new Set(value.concat(list)).size === list.length);
  241 + } else {
  242 + if (list.indexOf(value) > -1) {
  243 + result = true;
  244 + }
  245 + }
  246 +
  247 + if (!result) {
  248 + return formatMessage(rule, errorMessage || message['enum']);
  249 + }
  250 +
  251 + return null
  252 + },
  253 +
  254 + rangeNumber(rule, value, message) {
  255 + if (!types.number(value)) {
  256 + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
  257 + }
  258 +
  259 + let { minimum, maximum, exclusiveMinimum, exclusiveMaximum } = rule;
  260 + let min = exclusiveMinimum ? value <= minimum : value < minimum;
  261 + let max = exclusiveMaximum ? value >= maximum : value > maximum;
  262 +
  263 + if (minimum !== undefined && min) {
  264 + return formatMessage(rule, rule.errorMessage || message['number'].min)
  265 + } else if (maximum !== undefined && max) {
  266 + return formatMessage(rule, rule.errorMessage || message['number'].max)
  267 + } else if (minimum !== undefined && maximum !== undefined && (min || max)) {
  268 + return formatMessage(rule, rule.errorMessage || message['number'].range)
  269 + }
  270 +
  271 + return null
  272 + },
  273 +
  274 + rangeLength(rule, value, message) {
  275 + if (!types.string(value) && !types.array(value)) {
  276 + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
  277 + }
  278 +
  279 + let min = rule.minLength;
  280 + let max = rule.maxLength;
  281 + let val = value.length;
  282 +
  283 + if (min !== undefined && val < min) {
  284 + return formatMessage(rule, rule.errorMessage || message['length'].min)
  285 + } else if (max !== undefined && val > max) {
  286 + return formatMessage(rule, rule.errorMessage || message['length'].max)
  287 + } else if (min !== undefined && max !== undefined && (val < min || val > max)) {
  288 + return formatMessage(rule, rule.errorMessage || message['length'].range)
  289 + }
  290 +
  291 + return null
  292 + },
  293 +
  294 + pattern(rule, value, message) {
  295 + if (!types['pattern'](rule.pattern, value)) {
  296 + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
  297 + }
  298 +
  299 + return null
  300 + },
  301 +
  302 + format(rule, value, message) {
  303 + var customTypes = Object.keys(types);
  304 + var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : rule.format;
  305 +
  306 + if (customTypes.indexOf(format) > -1) {
  307 + if (!types[format](value)) {
  308 + return formatMessage(rule, rule.errorMessage || message.types[format]);
  309 + }
  310 + }
  311 +
  312 + return null
  313 + }
  314 +}
  315 +
  316 +class SchemaValidator extends RuleValidator {
  317 +
  318 + constructor(schema, options) {
  319 + super(SchemaValidator.message);
  320 +
  321 + this._schema = schema
  322 + this._options = options || null
  323 + }
  324 +
  325 + updateSchema(schema) {
  326 + this._schema = schema
  327 + }
  328 +
  329 + async validate(data, allData) {
  330 + let result = this._checkFieldInSchema(data)
  331 + if (!result) {
  332 + result = await this.invokeValidate(data, false, allData)
  333 + }
  334 + return result.length ? result[0] : null
  335 + }
  336 +
  337 + async validateAll(data, allData) {
  338 + let result = this._checkFieldInSchema(data)
  339 + if (!result) {
  340 + result = await this.invokeValidate(data, true, allData)
  341 + }
  342 + return result
  343 + }
  344 +
  345 + async validateUpdate(data, allData) {
  346 + let result = this._checkFieldInSchema(data)
  347 + if (!result) {
  348 + result = await this.invokeValidateUpdate(data, false, allData)
  349 + }
  350 + return result.length ? result[0] : null
  351 + }
  352 +
  353 + async invokeValidate(data, all, allData) {
  354 + let result = []
  355 + let schema = this._schema
  356 + for (let key in schema) {
  357 + let value = schema[key]
  358 + let errorMessage = await this.validateRule(value, data[key], data, allData)
  359 + if (errorMessage != null) {
  360 + result.push({
  361 + key,
  362 + errorMessage
  363 + })
  364 + if (!all) break
  365 + }
  366 + }
  367 + return result
  368 + }
  369 +
  370 + async invokeValidateUpdate(data, all, allData) {
  371 + let result = []
  372 + for (let key in data) {
  373 + let errorMessage = await this.validateRule(this._schema[key], data[key], data, allData)
  374 + if (errorMessage != null) {
  375 + result.push({
  376 + key,
  377 + errorMessage
  378 + })
  379 + if (!all) break
  380 + }
  381 + }
  382 + return result
  383 + }
  384 +
  385 + _checkFieldInSchema(data) {
  386 + var keys = Object.keys(data)
  387 + var keys2 = Object.keys(this._schema)
  388 + if (new Set(keys.concat(keys2)).size === keys2.length) {
  389 + return ''
  390 + }
  391 + return [{
  392 + key: 'invalid',
  393 + errorMessage: SchemaValidator.message['defaultInvalid']
  394 + }]
  395 + }
  396 +}
  397 +
  398 +function Message() {
  399 + return {
  400 + default: '验证错误',
  401 + defaultInvalid: '字段超出范围',
  402 + required: '{label}必填',
  403 + 'enum': '{label}超出范围',
  404 + whitespace: '{label}不能为空',
  405 + date: {
  406 + format: '{label}日期{value}格式无效',
  407 + parse: '{label}日期无法解析,{value}无效',
  408 + invalid: '{label}日期{value}无效'
  409 + },
  410 + types: {
  411 + string: '{label}类型无效',
  412 + array: '{label}类型无效',
  413 + object: '{label}类型无效',
  414 + number: '{label}类型无效',
  415 + date: '{label}类型无效',
  416 + boolean: '{label}类型无效',
  417 + integer: '{label}类型无效',
  418 + float: '{label}类型无效',
  419 + regexp: '{label}无效',
  420 + email: '{label}类型无效',
  421 + url: '{label}类型无效'
  422 + },
  423 + length: {
  424 + min: '{label}长度不能少于{minLength}',
  425 + max: '{label}长度不能超过{maxLength}',
  426 + range: '{label}必须介于{minLength}和{maxLength}之间'
  427 + },
  428 + number: {
  429 + min: '{label}不能小于{minimum}',
  430 + max: '{label}不能大于{maximum}',
  431 + range: '{label}必须介于{minimum}and{maximum}之间'
  432 + },
  433 + pattern: {
  434 + mismatch: '{label}格式不匹配'
  435 + }
  436 + };
  437 +}
  438 +
  439 +
  440 +SchemaValidator.message = new Message();
  441 +
  442 +export default SchemaValidator
components/usp-tinymce/dynamicLoadScript.js 0 → 100644
  1 +let callbacks = []
  2 +
  3 +function loadedTinymce() {
  4 + // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
  5 + // check is successfully downloaded script
  6 + return window.tinymce
  7 +}
  8 +
  9 +const dynamicLoadScript = (src, callback) => {
  10 + const existingScript = document.getElementById(src)
  11 + const cb = callback || function() {}
  12 +
  13 + if (!existingScript) {
  14 + const script = document.createElement('script')
  15 + script.src = src // src url for the third-party library being loaded.
  16 + script.id = src
  17 + document.body.appendChild(script)
  18 + callbacks.push(cb)
  19 + const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
  20 + onEnd(script)
  21 + }
  22 +
  23 + if (existingScript && cb) {
  24 + if (loadedTinymce()) {
  25 + cb(null, existingScript)
  26 + } else {
  27 + callbacks.push(cb)
  28 + }
  29 + }
  30 +
  31 + function stdOnEnd(script) {
  32 + script.onload = function() {
  33 + // this.onload = null here is necessary
  34 + // because even IE9 works not like others
  35 + this.onerror = this.onload = null
  36 + for (const cb of callbacks) {
  37 + cb(null, script)
  38 + }
  39 + callbacks = null
  40 + }
  41 + script.onerror = function() {
  42 + this.onerror = this.onload = null
  43 + cb(new Error('Failed to load ' + src), script)
  44 + }
  45 + }
  46 +
  47 + function ieOnEnd(script) {
  48 + script.onreadystatechange = function() {
  49 + if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
  50 + this.onreadystatechange = null
  51 + for (const cb of callbacks) {
  52 + cb(null, script) // there is no way to catch loading errors in IE8
  53 + }
  54 + callbacks = null
  55 + }
  56 + }
  57 +}
  58 +
  59 +export default dynamicLoadScript
components/usp-tinymce/plugins.js 0 → 100644
  1 +// Any plugins you want to use has to be imported
  2 +// Detail plugins list see https://www.tinymce.com/docs/plugins/
  3 +// Custom builds see https://www.tinymce.com/download/custom-builds/
  4 +
  5 +const plugins = [
  6 + `
  7 + advlist
  8 + autolink
  9 + autosave
  10 + code
  11 + codesample
  12 + colorpicker
  13 + colorpicker
  14 + contextmenu
  15 + directionality
  16 + emoticons
  17 + fullscreen
  18 + hr
  19 + image
  20 + imagetools
  21 + insertdatetime
  22 + lists
  23 + media
  24 + nonbreaking
  25 + noneditable
  26 + paste
  27 + preview
  28 + print
  29 + save
  30 + searchreplace
  31 + tabfocus
  32 + table
  33 + textcolor
  34 + textpattern
  35 + visualblocks
  36 + visualchars
  37 + wordcount
  38 + `
  39 +]
  40 +
  41 +export default plugins
components/usp-tinymce/toolbar.js 0 → 100644
  1 +// Here is a list of the toolbar
  2 +// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
  3 +
  4 +const toolbar = [
  5 + 'bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript',
  6 + 'hr bullist numlist image table forecolor backcolor code preview fullscreen'
  7 +]
  8 +
  9 +export default toolbar
components/usp-tinymce/usp-tinymce.vue 0 → 100644
  1 +<template>
  2 + <div :class="{fullscreen:fullscreen, loaded: loaded}" class="tinymce-container" :style="{width:containerWidth}">
  3 + <textarea :id="tinymceId" class="tinymce-textarea" />
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +import plugins from './plugins'
  9 +import toolbar from './toolbar'
  10 +import load from './dynamicLoadScript'
  11 +
  12 +const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
  13 +
  14 +export default {
  15 + name: 'uspTinymce',
  16 + props: {
  17 + id: {
  18 + type: String,
  19 + default() {
  20 + return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
  21 + }
  22 + },
  23 + value: {
  24 + type: String,
  25 + default: ''
  26 + },
  27 + toolbar: {
  28 + type: Array,
  29 + required: false,
  30 + default() {
  31 + return []
  32 + }
  33 + },
  34 + menubar: {
  35 + type: String,
  36 + default: 'file edit insert view format table'
  37 + },
  38 + height: {
  39 + type: [Number, String],
  40 + required: false,
  41 + default: 360
  42 + },
  43 + width: {
  44 + type: [Number, String],
  45 + required: false,
  46 + default: 'auto'
  47 + },
  48 + isHtml:{
  49 + type: Boolean,
  50 + default: true
  51 + }
  52 + },
  53 + data() {
  54 + return {
  55 + loaded: false,
  56 + hasChange: false,
  57 + hasInit: false,
  58 + tinymceId: this.id,
  59 + fullscreen: false,
  60 + languageTypeList: {
  61 + 'en': 'en',
  62 + 'zh': 'zh_CN',
  63 + 'es': 'es_MX',
  64 + 'ja': 'ja'
  65 + }
  66 + }
  67 + },
  68 + computed: {
  69 + containerWidth() {
  70 + const width = this.width
  71 + if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
  72 + return `${width}px`
  73 + }
  74 + return width
  75 + }
  76 + },
  77 + watch: {
  78 + value(val) {
  79 + if (!this.hasChange && this.hasInit) {
  80 + this.$nextTick(() =>window.tinymce.get(this.tinymceId).setContent(val || ''))
  81 + }
  82 + }
  83 + },
  84 + mounted() {
  85 + this.init()
  86 + },
  87 + activated() {
  88 + if (window.tinymce) {
  89 + this.initTinymce()
  90 + }
  91 + },
  92 + deactivated() {
  93 + this.destroyTinymce()
  94 + },
  95 + destroyed() {
  96 + this.destroyTinymce()
  97 + },
  98 + methods: {
  99 + init() {
  100 + // dynamic load tinymce from cdn
  101 + load(tinymceCDN, (err) => {
  102 + if (err) {
  103 + this.$message.error(err.message)
  104 + return
  105 + }
  106 + this.initTinymce()
  107 + })
  108 + },
  109 + initTinymce() {
  110 + const _this = this
  111 + window.tinymce.init({
  112 + selector: `#${this.tinymceId}`,
  113 + images_upload_handler: async (blobInfo, succFun, failFun, progress)=> {
  114 + progress(0);
  115 + let fileName = + new Date() + ('000000' + Math.floor(Math.random() * 999999)).slice(-6);
  116 + fileName += blobInfo.blob().type === 'image/jpeg' ? '.jpg' : '.png';
  117 + const result = await uniCloud.uploadFile({
  118 + filePath: blobInfo.blobUri(),
  119 + cloudPath: fileName,
  120 + onUploadProgress: progressEvent=> {
  121 + progress(Math.round(
  122 + (progressEvent.loaded * 100) / progressEvent.total
  123 + ));
  124 + }
  125 + });
  126 +
  127 + const tempFiles = await uniCloud.getTempFileURL({
  128 + fileList: [result.fileID]
  129 + })
  130 + const tempFile = tempFiles.fileList[0];
  131 + if(tempFile.code === 'SUCCESS'){
  132 + succFun(tempFile.download_url);
  133 + }else{
  134 + failFun('上传失败');
  135 + }
  136 + },
  137 + language: this.languageTypeList['zh'],
  138 + height: this.height,
  139 + body_class: 'panel-body ',
  140 + object_resizing: false,
  141 + toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
  142 + menubar: this.menubar,
  143 + plugins: plugins,
  144 + end_container_on_empty_block: true,
  145 + powerpaste_word_import: 'clean',
  146 + code_dialog_height: 450,
  147 + code_dialog_width: 1000,
  148 + advlist_bullet_styles: 'square',
  149 + advlist_number_styles: 'default',
  150 + imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
  151 + default_link_target: '_blank',
  152 + link_title: false,
  153 + nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
  154 + init_instance_callback: editor => {
  155 + //if (_this.value) {
  156 + editor.setContent(_this.value || '')
  157 + setTimeout(()=>{
  158 + this.loaded = true;
  159 + }, 100)
  160 + //}
  161 + _this.hasInit = true
  162 + editor.on('NodeChange Change KeyUp SetContent', () => {
  163 + this.hasChange = true
  164 + if(this.isHtml){
  165 + this.$emit('input', editor.getContent())
  166 + }else{
  167 + this.$emit('input', editor.getContent({format: 'text'}))
  168 + }
  169 + })
  170 + },
  171 + setup(editor) {
  172 + editor.on('FullscreenStateChanged', (e) => {
  173 + _this.fullscreen = e.state
  174 + })
  175 + }
  176 + })
  177 + },
  178 + destroyTinymce() {
  179 + const tinymce = window.tinymce.get(this.tinymceId)
  180 + if (this.fullscreen) {
  181 + tinymce.execCommand('mceFullScreen')
  182 + }
  183 +
  184 + if (tinymce) {
  185 + tinymce.destroy()
  186 + }
  187 + },
  188 + setContent(value) {
  189 + window.tinymce.get(this.tinymceId).setContent(value)
  190 + },
  191 + getContent() {
  192 + window.tinymce.get(this.tinymceId).getContent()
  193 + },
  194 + imageSuccessCBK(arr) {
  195 + const _this = this
  196 + arr.forEach(v => {
  197 + window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
  198 + })
  199 + }
  200 + }
  201 +}
  202 +</script>
  203 +
  204 +<style scoped lang="scss">
  205 + /deep/ {
  206 + .tinymce-container {
  207 + position: relative;
  208 + line-height: normal;
  209 + }
  210 + .tinymce-container>>>.mce-fullscreen {
  211 + z-index: 10000;
  212 + }
  213 + .tinymce-textarea {
  214 + visibility: hidden;
  215 + z-index: -1;
  216 + }
  217 + .editor-custom-btn-container {
  218 + position: absolute;
  219 + right: 4px;
  220 + top: 4px;
  221 + /*z-index: 2005;*/
  222 + }
  223 + .fullscreen .editor-custom-btn-container {
  224 + z-index: 10000;
  225 + position: fixed;
  226 + }
  227 + .editor-upload-btn {
  228 + display: inline-block;
  229 + }
  230 + .mce-tinymce{
  231 + box-shadow: 0 1px 2px rgba(0, 0, 0, 0) !important;
  232 + border-color: #DCDFE6 !important;
  233 + }
  234 + .mce-top-part::before{
  235 + box-shadow: 0 1px 2px rgba(0, 0, 0, 0) !important;
  236 + }
  237 + .mce-edit-area{
  238 + border-color: #DCDFE6 !important;
  239 +
  240 + iframe{
  241 + opacity: 0;
  242 + }
  243 + }
  244 + .mce-statusbar{
  245 + border-color: #DCDFE6 !important;
  246 + }
  247 + #mceu_27{
  248 + border-radius: 5px;
  249 + overflow: hidden;
  250 + }
  251 + /* 菜单图标字体颜色 */
  252 + .mce-ico{
  253 + color: #7e8086 !important;
  254 + }
  255 + /* 选中项图标为白色 */
  256 + .mce-btn.mce-active i{
  257 + color: #fff !important;
  258 + }
  259 + /* 背景颜色图标 */
  260 + i.mce-i-backcolor{
  261 + color: #fff !important;
  262 + background-color: #ff944c !important;
  263 + }
  264 + /* 字体颜色图标 */
  265 + i.mce-i-forecolor{
  266 + color: #ff944c !important;
  267 + transform: scale(1.1);
  268 + }
  269 + }
  270 + .loaded /deep/ iframe{
  271 + opacity: 1;
  272 + }
  273 +</style>
0 \ No newline at end of file 274 \ No newline at end of file
pages.json
1 -{  
2 - "easycom": {  
3 - "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"  
4 - },  
5 - "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages  
6 - {  
7 - "path": "pages/home/home",  
8 - "style": {  
9 - "navigationStyle": "custom"  
10 - }  
11 - },{  
12 - "path": "pages/login/login",  
13 - "style": {  
14 - "navigationStyle": "custom"  
15 - }  
16 - },{  
17 - "path": "pages/workbench/workbench",  
18 - "style": {  
19 - "navigationStyle": "custom"  
20 - }  
21 - }, {  
22 - "path": "pages/my/my",  
23 - "style": {  
24 - "navigationStyle": "custom"  
25 - }  
26 - },{  
27 - "path": "pages/apply/apply",  
28 - "style": {  
29 - "navigationBarTitleText": "申请记录",  
30 - "navigationBarBackgroundColor": "#FFFFFF"  
31 - }  
32 - },{  
33 - "path": "pages/shops/shops",  
34 - "style": {  
35 - "navigationBarTitleText": "租商铺",  
36 - "navigationBarBackgroundColor": "#FFFFFF"  
37 - }  
38 - },{  
39 - "path": "pages/applyDetail/applyDetail",  
40 - "style": {  
41 - "navigationBarTitleText": "详情",  
42 - "navigationBarBackgroundColor": "#FFFFFF"  
43 - }  
44 - },{  
45 - "path": "pages/message/message",  
46 - "style": {  
47 - "navigationStyle": "custom"  
48 - }  
49 - },{  
50 - "path": "pages/field/field",  
51 - "style": {  
52 - "navigationBarTitleText": "租场地",  
53 - "navigationBarBackgroundColor": "#FFFFFF"  
54 - }  
55 - },{  
56 - "path": "pages/advertisementDetail/advertisementDetail",  
57 - "style": {  
58 - "navigationBarTitleText": "详情",  
59 - "navigationBarBackgroundColor": "#FFFFFF"  
60 - }  
61 - },{  
62 - "path": "pages/advertisement/advertisement",  
63 - "style": {  
64 - "navigationBarTitleText": "租广告",  
65 - "navigationBarBackgroundColor": "#FFFFFF"  
66 - }  
67 - },{  
68 - "path": "pages/details/details",  
69 - "style": {  
70 - "navigationBarTitleText": "详情",  
71 - "navigationBarBackgroundColor": "#FFFFFF"  
72 - }  
73 - },{  
74 - "path": "pages/leaseAdd/leaseAdd",  
75 - "style": {  
76 - "navigationBarTitleText": "申请租赁",  
77 - "navigationBarBackgroundColor": "#FFFFFF"  
78 - }  
79 - },  
80 - {  
81 - "path": "pages/intentionApply/intentionApply",  
82 - "style": {  
83 - "navigationBarTitleText": "意向申请",  
84 - "navigationBarBackgroundColor": "#FFFFFF"  
85 - }  
86 - },  
87 - {  
88 - "path": "pages/recordService/recordService",  
89 - "style": {  
90 - "navigationBarTitleText": "服务记录",  
91 - "navigationBarBackgroundColor": "#FFFFFF"  
92 - }  
93 - },{  
94 - "path": "pages/activityAdd/activityAdd",  
95 - "style": {  
96 - "navigationBarTitleText": "活动申请",  
97 - "navigationBarBackgroundColor": "#FFFFFF"  
98 - }  
99 - },{  
100 - "path": "pages/complaint/complaint",  
101 - "style": {  
102 - "navigationBarTitleText": "投诉建议",  
103 - "navigationBarBackgroundColor": "#FFFFFF"  
104 - }  
105 - },{  
106 - "path": "pages/repair/repair",  
107 - "style": {  
108 - "navigationBarTitleText": "故障报修",  
109 - "navigationBarBackgroundColor": "#FFFFFF"  
110 - }  
111 - },{  
112 - "path": "pages/advertisementTime/advertisementTime",  
113 - "style": {  
114 - "navigationBarTitleText": "租赁时段选择",  
115 - "navigationBarBackgroundColor": "#FFFFFF"  
116 - }  
117 - },{  
118 - "path": "pages/advertisementAdd/advertisementAdd",  
119 - "style": {  
120 - "navigationBarTitleText": "广告申请",  
121 - "navigationBarBackgroundColor": "#FFFFFF"  
122 - }  
123 - },  
124 - {  
125 - "path": "pages/participation/participation",  
126 - "style": {  
127 - "navigationBarTitleText": "活动参与",  
128 - "navigationBarBackgroundColor": "#fff",  
129 - "enablePullDownRefresh": true  
130 - }  
131 - },  
132 - {  
133 - "path": "pages/questionnaire/questionnaire",  
134 - "style": {  
135 - "navigationBarTitleText": "问卷调查",  
136 - "navigationBarBackgroundColor": "#FFFFFF"  
137 - }  
138 - },  
139 - {  
140 - "path": "pages/createQuestionnaire/createQuestionnaire",  
141 - "style": {  
142 - "navigationBarTitleText": "创建问卷",  
143 - "navigationBarBackgroundColor": "#FFFFFF"  
144 - }  
145 - },  
146 - {  
147 - "path": "pages/mycreated/mycreated",  
148 - "style": {  
149 - "navigationBarTitleText": "我的活动申请",  
150 - "navigationBarBackgroundColor": "#FFFFFF",  
151 - "enablePullDownRefresh": true  
152 - }  
153 - },  
154 - {  
155 - "path": "pages/record/record",  
156 - "style": {  
157 - "navigationBarTitleText": "申请记录",  
158 - "navigationBarBackgroundColor": "#FFFFFF"  
159 - }  
160 - },  
161 - {  
162 - "path": "pages/accepting/accepting",  
163 - "style": {  
164 - "navigationBarTitleText": "详情",  
165 - "navigationBarBackgroundColor": "#FFFFFF"  
166 - }  
167 - },  
168 - {  
169 - "path": "pages/servicerecords/servicerecords",  
170 - "style": {  
171 - "navigationBarTitleText": "服务记录",  
172 - "navigationBarBackgroundColor": "#FFFFFF"  
173 - }  
174 - },  
175 - {  
176 - "path": "pages/servicedetails/servicedetails",  
177 - "style": {  
178 - "navigationBarTitleText": "详情",  
179 - "navigationBarBackgroundColor": "#FFFFFF"  
180 - }  
181 - },  
182 - {  
183 - "path": "pages/application/application",  
184 - "style": {  
185 - "navigationBarTitleText": "推广方案申请",  
186 - "navigationBarBackgroundColor": "#FFFFFF"  
187 - }  
188 - },  
189 - {  
190 - "path": "pages/projectManagement/projectManagement",  
191 - "style": {  
192 - "navigationBarTitleText": "推广方案管理",  
193 - "navigationBarBackgroundColor": "#FFFFFF"  
194 - }  
195 - },  
196 - {  
197 - "path": "pages/shopjcMsg/shopjcMsg",  
198 - "style": {  
199 - "navigationBarTitleText": "商家基本信息",  
200 - "navigationBarBackgroundColor": "#FFFFFF"  
201 - }  
202 - },  
203 - {  
204 - "path": "pages/procedure/procedure",  
205 - "style": {  
206 - "navigationBarTitleText": "公告通知",  
207 - "navigationBarBackgroundColor": "#FFFFFF"  
208 - }  
209 - },  
210 - {  
211 - "path": "pages/salesReporting/salesReporting",  
212 - "style": {  
213 - "navigationBarTitleText": "销售上报",  
214 - "navigationBarBackgroundColor": "#FFFFFF"  
215 - }  
216 - },  
217 - {  
218 - "path": "pages/orderList/orderList",  
219 - "style": {  
220 - "navigationBarTitleText": "订单查询",  
221 - "navigationBarBackgroundColor": "#FFFFFF"  
222 - }  
223 - },  
224 - {  
225 - "path": "pages/salesSta/salesSta",  
226 - "style": {  
227 - "navigationBarTitleText": "销售统计",  
228 - "navigationBarBackgroundColor": "#FFFFFF"  
229 - }  
230 - },  
231 - {  
232 - "path": "pages/Iproposal/Iproposal",  
233 - "style": {  
234 - "navigationBarTitleText": "招商方案",  
235 - "navigationBarBackgroundColor": "#FFFFFF"  
236 - }  
237 - },  
238 - // 营销推广活动  
239 - {  
240 - "path": "pages/marketing/marketingList/marketingList",  
241 - "style": {  
242 - "navigationBarTitleText": "营销推广活动",  
243 - "navigationBarBackgroundColor": "#FFFFFF"  
244 - }  
245 - },  
246 - {  
247 - "path": "pages/marketing/marketingDetail/marketingDetail",  
248 - "style": {  
249 - "navigationBarTitleText": "详情",  
250 - "navigationBarBackgroundColor": "#FFFFFF"  
251 - }  
252 - },  
253 - // 商务合作  
254 - {  
255 - "path": "pages/business/businessList/businessList",  
256 - "style": {  
257 - "navigationBarTitleText": "商务合作",  
258 - "navigationBarBackgroundColor": "#FFFFFF"  
259 - }  
260 - },  
261 - {  
262 - "path": "pages/business/businessDetail/businessDetail",  
263 - "style": {  
264 - "navigationBarTitleText": "详情",  
265 - "navigationBarBackgroundColor": "#FFFFFF"  
266 - }  
267 - },  
268 - // 物业缴费  
269 - {  
270 - "path": "pages/propertyPay/propertyPayList/propertyPayList",  
271 - "style": {  
272 - "navigationBarTitleText": "物业缴费",  
273 - "navigationBarBackgroundColor": "#FFFFFF"  
274 - }  
275 - },  
276 - {  
277 - "path": "pages/propertyPay/payRecord/payRecord",  
278 - "style": {  
279 - "navigationBarTitleText": "缴费记录",  
280 - "navigationBarBackgroundColor": "#FFFFFF"  
281 - }  
282 - },  
283 - {  
284 - "path": "pages/propertyPay/payDetail/payDetail",  
285 - "style": {  
286 - "navigationBarTitleText": "缴费记录",  
287 - "navigationBarBackgroundColor": "#FFFFFF"  
288 - }  
289 - }  
290 - ],  
291 - "globalStyle": {  
292 - "navigationBarTextStyle": "black"  
293 - },  
294 - "tabBar": {  
295 - "custom": true,  
296 - "selectedColor": "#F15A29",  
297 - "backgroundColor": "#fff",  
298 - "list": [{  
299 - "iconPath": "/static/tabbar/tab_01.png",  
300 - "selectedIconPath": "/static/tabbar/tab_02.png",  
301 - "pagePath": "pages/home/home",  
302 - "text": ""  
303 - },  
304 - {  
305 - "selectedIconPath": "/static/tabbar/tab_03.png",  
306 - "iconPath": "/static/tabbar/tab_04.png",  
307 - "pagePath": "pages/workbench/workbench",  
308 - "text": ""  
309 - },  
310 - {  
311 - "iconPath": "/static/tabbar/tab_05.png",  
312 - "selectedIconPath": "/static/tabbar/tab_06.png",  
313 - "pagePath": "pages/message/message",  
314 - "text": ""  
315 - },  
316 - {  
317 - "iconPath": "/static/tabbar/tab_07.png",  
318 - "selectedIconPath": "/static/tabbar/tab_08.png",  
319 - "pagePath": "pages/my/my",  
320 - "text": ""  
321 - }  
322 - ]  
323 - }  
324 -} 1 +{
  2 + "easycom": {
  3 + "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
  4 + },
  5 + "pages": [
  6 + {
  7 + "path": "pages/home/home",
  8 + "style": {
  9 + "navigationStyle": "custom"
  10 + }
  11 + },
  12 + {
  13 + "path": "pages/login/login",
  14 + "style": {
  15 + "navigationStyle": "custom"
  16 + }
  17 + },
  18 + {
  19 + "path": "pages/workbench/workbench",
  20 + "style": {
  21 + "navigationStyle": "custom"
  22 + }
  23 + },
  24 + {
  25 + "path": "pages/my/my",
  26 + "style": {
  27 + "navigationStyle": "custom"
  28 + }
  29 + },
  30 + {
  31 + "path": "pages/apply/apply",
  32 + "style": {
  33 + "navigationBarTitleText": "申请记录",
  34 + "navigationBarBackgroundColor": "#FFFFFF"
  35 + }
  36 + },
  37 + {
  38 + "path": "pages/shops/shops",
  39 + "style": {
  40 + "navigationBarTitleText": "租商铺",
  41 + "navigationBarBackgroundColor": "#FFFFFF"
  42 + }
  43 + },
  44 + {
  45 + "path": "pages/applyDetail/applyDetail",
  46 + "style": {
  47 + "navigationBarTitleText": "详情",
  48 + "navigationBarBackgroundColor": "#FFFFFF"
  49 + }
  50 + },
  51 + {
  52 + "path": "pages/message/message",
  53 + "style": {
  54 + "navigationStyle": "custom"
  55 + }
  56 + },
  57 + {
  58 + "path": "pages/field/field",
  59 + "style": {
  60 + "navigationBarTitleText": "租场地",
  61 + "navigationBarBackgroundColor": "#FFFFFF"
  62 + }
  63 + },
  64 + {
  65 + "path": "pages/advertisementDetail/advertisementDetail",
  66 + "style": {
  67 + "navigationBarTitleText": "详情",
  68 + "navigationBarBackgroundColor": "#FFFFFF"
  69 + }
  70 + },
  71 + {
  72 + "path": "pages/advertisement/advertisement",
  73 + "style": {
  74 + "navigationBarTitleText": "租广告",
  75 + "navigationBarBackgroundColor": "#FFFFFF"
  76 + }
  77 + },
  78 + {
  79 + "path": "pages/details/details",
  80 + "style": {
  81 + "navigationBarTitleText": "详情",
  82 + "navigationBarBackgroundColor": "#FFFFFF"
  83 + }
  84 + },
  85 + {
  86 + "path": "pages/leaseAdd/leaseAdd",
  87 + "style": {
  88 + "navigationBarTitleText": "申请租赁",
  89 + "navigationBarBackgroundColor": "#FFFFFF"
  90 + }
  91 + },
  92 + {
  93 + "path": "pages/intentionApply/intentionApply",
  94 + "style": {
  95 + "navigationBarTitleText": "意向申请",
  96 + "navigationBarBackgroundColor": "#FFFFFF"
  97 + }
  98 + },
  99 + {
  100 + "path": "pages/recordService/recordService",
  101 + "style": {
  102 + "navigationBarTitleText": "服务记录",
  103 + "navigationBarBackgroundColor": "#FFFFFF"
  104 + }
  105 + },
  106 + {
  107 + "path": "pages/activityAdd/activityAdd",
  108 + "style": {
  109 + "navigationBarTitleText": "活动申请",
  110 + "navigationBarBackgroundColor": "#FFFFFF"
  111 + }
  112 + },
  113 + {
  114 + "path": "pages/complaint/complaint",
  115 + "style": {
  116 + "navigationBarTitleText": "投诉建议",
  117 + "navigationBarBackgroundColor": "#FFFFFF"
  118 + }
  119 + },
  120 + {
  121 + "path": "pages/repair/repair",
  122 + "style": {
  123 + "navigationBarTitleText": "故障报修",
  124 + "navigationBarBackgroundColor": "#FFFFFF"
  125 + }
  126 + },
  127 + {
  128 + "path": "pages/advertisementTime/advertisementTime",
  129 + "style": {
  130 + "enablePullDownRefresh": false,
  131 + "navigationBarTitleText": "投放时段",
  132 + "navigationBarBackgroundColor": "#FFFFFF"
  133 + }
  134 + },
  135 + {
  136 + "path": "pages/advertisementAdd/advertisementAdd",
  137 + "style": {
  138 + "navigationBarTitleText": "广告申请",
  139 + "navigationBarBackgroundColor": "#FFFFFF"
  140 + }
  141 + },
  142 + {
  143 + "path": "pages/participation/participation",
  144 + "style": {
  145 + "navigationBarTitleText": "活动参与",
  146 + "navigationBarBackgroundColor": "#fff",
  147 + "enablePullDownRefresh": true
  148 + }
  149 + },
  150 + {
  151 + "path": "pages/questionnaire/questionnaire",
  152 + "style": {
  153 + "navigationBarTitleText": "问卷调查",
  154 + "navigationBarBackgroundColor": "#FFFFFF"
  155 + }
  156 + },
  157 + {
  158 + "path": "pages/createQuestionnaire/createQuestionnaire",
  159 + "style": {
  160 + "navigationBarTitleText": "创建问卷",
  161 + "navigationBarBackgroundColor": "#FFFFFF"
  162 + }
  163 + },
  164 + {
  165 + "path": "pages/mycreated/mycreated",
  166 + "style": {
  167 + "navigationBarTitleText": "我的活动申请",
  168 + "navigationBarBackgroundColor": "#FFFFFF",
  169 + "enablePullDownRefresh": true
  170 + }
  171 + },
  172 + {
  173 + "path": "pages/record/record",
  174 + "style": {
  175 + "navigationBarTitleText": "申请记录",
  176 + "navigationBarBackgroundColor": "#FFFFFF"
  177 + }
  178 + },
  179 + {
  180 + "path": "pages/accepting/accepting",
  181 + "style": {
  182 + "navigationBarTitleText": "详情",
  183 + "navigationBarBackgroundColor": "#FFFFFF"
  184 + }
  185 + },
  186 + {
  187 + "path": "pages/servicerecords/servicerecords",
  188 + "style": {
  189 + "navigationBarTitleText": "服务记录",
  190 + "navigationBarBackgroundColor": "#FFFFFF"
  191 + }
  192 + },
  193 + {
  194 + "path": "pages/servicedetails/servicedetails",
  195 + "style": {
  196 + "navigationBarTitleText": "详情",
  197 + "navigationBarBackgroundColor": "#FFFFFF"
  198 + }
  199 + },
  200 + {
  201 + "path": "pages/application/application",
  202 + "style": {
  203 + "navigationBarTitleText": "推广方案申请",
  204 + "navigationBarBackgroundColor": "#FFFFFF"
  205 + }
  206 + },
  207 + {
  208 + "path": "pages/projectManagement/projectManagement",
  209 + "style": {
  210 + "navigationBarTitleText": "推广方案管理",
  211 + "navigationBarBackgroundColor": "#FFFFFF"
  212 + }
  213 + },
  214 + {
  215 + "path": "pages/shopjcMsg/shopjcMsg",
  216 + "style": {
  217 + "navigationBarTitleText": "商家基本信息",
  218 + "navigationBarBackgroundColor": "#FFFFFF"
  219 + }
  220 + },
  221 + {
  222 + "path": "pages/procedure/procedure",
  223 + "style": {
  224 + "navigationBarTitleText": "公告通知",
  225 + "navigationBarBackgroundColor": "#FFFFFF"
  226 + }
  227 + },
  228 + {
  229 + "path": "pages/salesReporting/salesReporting",
  230 + "style": {
  231 + "navigationBarTitleText": "销售上报",
  232 + "navigationBarBackgroundColor": "#FFFFFF"
  233 + }
  234 + },
  235 + {
  236 + "path": "pages/orderList/orderList",
  237 + "style": {
  238 + "navigationBarTitleText": "订单查询",
  239 + "navigationBarBackgroundColor": "#FFFFFF"
  240 + }
  241 + },
  242 + {
  243 + "path": "pages/salesSta/salesSta",
  244 + "style": {
  245 + "navigationBarTitleText": "销售统计",
  246 + "navigationBarBackgroundColor": "#FFFFFF"
  247 + }
  248 + },
  249 + {
  250 + "path": "pages/Iproposal/Iproposal",
  251 + "style": {
  252 + "navigationBarTitleText": "招商方案",
  253 + "navigationBarBackgroundColor": "#FFFFFF"
  254 + }
  255 + },
  256 + {
  257 + "path": "pages/marketing/marketingList/marketingList",
  258 + "style": {
  259 + "navigationBarTitleText": "营销推广活动",
  260 + "navigationBarBackgroundColor": "#FFFFFF"
  261 + }
  262 + },
  263 + {
  264 + "path": "pages/marketing/marketingDetail/marketingDetail",
  265 + "style": {
  266 + "navigationBarTitleText": "详情",
  267 + "navigationBarBackgroundColor": "#FFFFFF"
  268 + }
  269 + },
  270 + {
  271 + "path": "pages/business/businessList/businessList",
  272 + "style": {
  273 + "navigationBarTitleText": "商务合作",
  274 + "navigationBarBackgroundColor": "#FFFFFF"
  275 + }
  276 + },
  277 + {
  278 + "path": "pages/business/businessDetail/businessDetail",
  279 + "style": {
  280 + "navigationBarTitleText": "详情",
  281 + "navigationBarBackgroundColor": "#FFFFFF"
  282 + }
  283 + },
  284 + {
  285 + "path": "pages/propertyPay/propertyPayList/propertyPayList",
  286 + "style": {
  287 + "navigationBarTitleText": "物业缴费",
  288 + "navigationBarBackgroundColor": "#FFFFFF"
  289 + }
  290 + },
  291 + {
  292 + "path": "pages/propertyPay/payRecord/payRecord",
  293 + "style": {
  294 + "navigationBarTitleText": "缴费记录",
  295 + "navigationBarBackgroundColor": "#FFFFFF"
  296 + }
  297 + },
  298 + {
  299 + "path": "pages/propertyPay/payDetail/payDetail",
  300 + "style": {
  301 + "navigationBarTitleText": "缴费记录",
  302 + "navigationBarBackgroundColor": "#FFFFFF"
  303 + }
  304 + }
  305 +
  306 + ],
  307 + "globalStyle": {
  308 + "navigationBarTextStyle": "black"
  309 + },
  310 + "tabBar": {
  311 + "custom": true,
  312 + "selectedColor": "#F15A29",
  313 + "backgroundColor": "#fff",
  314 + "list": [
  315 + {
  316 + "iconPath": "/static/tabbar/tab_01.png",
  317 + "selectedIconPath": "/static/tabbar/tab_02.png",
  318 + "pagePath": "pages/home/home",
  319 + "text": ""
  320 + },
  321 + {
  322 + "selectedIconPath": "/static/tabbar/tab_03.png",
  323 + "iconPath": "/static/tabbar/tab_04.png",
  324 + "pagePath": "pages/workbench/workbench",
  325 + "text": ""
  326 + },
  327 + {
  328 + "iconPath": "/static/tabbar/tab_05.png",
  329 + "selectedIconPath": "/static/tabbar/tab_06.png",
  330 + "pagePath": "pages/message/message",
  331 + "text": ""
  332 + },
  333 + {
  334 + "iconPath": "/static/tabbar/tab_07.png",
  335 + "selectedIconPath": "/static/tabbar/tab_08.png",
  336 + "pagePath": "pages/my/my",
  337 + "text": ""
  338 + }
  339 + ]
  340 + }
  341 +}
325 \ No newline at end of file 342 \ No newline at end of file
uniCloud-tcb/cloudfunctions/usp-test/index.js 0 → 100644
  1 +'use strict';
  2 +exports.main = async (event, context) => {
  3 + //event为客户端上传的参数
  4 + console.log('event : ', event)
  5 +
  6 + //返回数据给客户端
  7 + return event
  8 +};
uniCloud-tcb/cloudfunctions/usp-test/package.json 0 → 100644
  1 +{
  2 + "name": "usp-test",
  3 + "origin-plugin-dev-name": "usp-tinymce",
  4 + "origin-plugin-version": "1.0.0",
  5 + "plugin-dev-name": "usp-tinymce",
  6 + "plugin-version": "1.0.0",
  7 + "description": ""
  8 +}
0 \ No newline at end of file 9 \ No newline at end of file
uni_modules/wn-calendar/components/wn-calendar/calendar.js 0 → 100644
  1 +/**
  2 + *
  3 + */
  4 +
  5 +function getDays(year, month, data, isLess) {
  6 + if (!Array.isArray(data)) {
  7 + data = []
  8 + }
  9 +
  10 + let today = new Date()
  11 +
  12 + let y, m
  13 + if (typeof(year) === 'number' && year > 2000 && typeof(month) === 'number') {
  14 + const d = new Date(year, month - 1)
  15 + y = d.getFullYear()
  16 + m = d.getMonth()
  17 + } else {
  18 + y = today.getFullYear()
  19 + m = today.getMonth()
  20 + }
  21 +
  22 + let st = new Date(y, m, 1).getDay(),
  23 + ed = new Date(y, m + 1, 0).getDay(),
  24 + len = new Date(y, m + 1, 0).getDate()
  25 +
  26 + let isfill = data.length > 0
  27 + let days = Array.from(new Array(len), (x, i) => {
  28 + i = i + 1
  29 + const date = `${y}/${m+1}/${i}`
  30 + x = null
  31 + if (isfill) {
  32 + x = data.find(item => item.date === date)
  33 + }
  34 + return {
  35 + show: true,
  36 + label: i,
  37 + date,
  38 + data: x
  39 + }
  40 + })
  41 +
  42 + let prev = new Date(y, m - 1),
  43 + prevDate = `${prev.getFullYear()}/${prev.getMonth()+1}`,
  44 + prevLd = new Date(y, m, 0).getDate()
  45 + let prevDays = Array.from(new Array(st), (x, i) => {
  46 + i = prevLd - (st - 1 - i)
  47 + return {
  48 + show: false,
  49 + label: isLess ? '' : i,
  50 + date: `${prevDate}/${i}`
  51 + }
  52 + })
  53 + days = prevDays.concat(days)
  54 +
  55 + let next = new Date(y, m + 1),
  56 + nextDate = `${next.getFullYear()}/${next.getMonth()+1}`
  57 + let lened = (days.length <= 35 ? 7 : 0) + (6 - ed)
  58 + if (isLess) {
  59 + lened = 6 - ed
  60 + }
  61 + let nextDays = Array.from(new Array(lened), (x, i) => {
  62 + i = i + 1
  63 + return {
  64 + show: false,
  65 + label: isLess ? '' : i,
  66 + date: `${nextDate}/${i}`
  67 + }
  68 + })
  69 + days = days.concat(nextDays)
  70 +
  71 + days = days.concat(Array.from(new Array(42 - days.length), (x, i) => {
  72 + return {
  73 + show: false,
  74 + label: '',
  75 + date: `*${i}`
  76 + }
  77 + }))
  78 +
  79 + return {
  80 + days,
  81 + year: y,
  82 + month: m
  83 + }
  84 +}
  85 +
  86 +function getEn (m) {
  87 + const en = [
  88 + 'Jan',
  89 + 'Feb',
  90 + 'Mar',
  91 + 'Apr',
  92 + 'May',
  93 + 'Jun',
  94 + 'Jul',
  95 + 'Aug',
  96 + 'Sept',
  97 + 'Oct',
  98 + 'Nov',
  99 + 'Dec',
  100 + ]
  101 + return en[m - 1]
  102 +}
  103 +
  104 +const labels_en = [
  105 + 'Sun',
  106 + 'Mon',
  107 + 'Tues',
  108 + 'Wed',
  109 + 'Thur',
  110 + 'Fri',
  111 + 'Sat',
  112 +]
  113 +
  114 +const labels_zh = [
  115 + '日',
  116 + '一',
  117 + '二',
  118 + '三',
  119 + '四',
  120 + '五',
  121 + '六',
  122 +]
  123 +
  124 +export default {
  125 + getDays,
  126 + getEn,
  127 + labels_en,
  128 + labels_zh
  129 +}
0 \ No newline at end of file 130 \ No newline at end of file
uni_modules/wn-calendar/components/wn-calendar/wn-calendar.vue 0 → 100644
  1 +<template>
  2 + <view class="wn-calendar">
  3 +
  4 + <view class="head">
  5 + <image @click="onChange('prev')"
  6 + src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAY5JREFUaEPtmEtugzAQhsc3aW5SbhI2SSOVCJ+g6QmoQqWWbspRcpNyELArQojUVqmw52EswYaNH/PNP57xWEHkn4rcflgAQiu4KBC9AsXxYw2qa6CFRutdIw2ECqGirA4K4GkwWp1s26XSECiAl7KyPzxuobGdSSQhaAF6GmEIegBhCB6AS1xZMInOdifOg80KcBbDqlTvNzUXBDvAEFHwrLPtgQNCBIATQgxghIDW1JRpVhSAA0IcgLpqBwKgu3oEBKCp2mEBCKp2eICxardm5ZOdZgMwiOF+9ZgVQN9T5NkmcanY8wKw0OT77SpaAJ+L32wU8In/czVxkev32D8tpedivsaHB+jbT2VSTNMTTgEC4wMq4J4ub0VnAAXojA+gAK3xsgDK1vnjQ+qZqG5OEwmhqJt6TI6foharAtzGs54BCeN5AIgK1JTwwQMcqy9QcHfdTPhlGg/w+v4JVq3HpxLXZmSql/8bhzrE/cJF+Xbf/zEXMgwIGgCzOcXcBYDCi5g1FgUw3qOYG70C39PtD0Aq3TXKAAAAAElFTkSuQmCC"
  7 + class="arrow left" mode="aspectFit"></image>
  8 + <text class="t">{{headText}}</text>
  9 + <image @click="onChange('next')"
  10 + src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAY5JREFUaEPtmEtugzAQhsc3aW5SbhI2SSOVCJ+g6QmoQqWWbspRcpNyELArQojUVqmw52EswYaNH/PNP57xWEHkn4rcflgAQiu4KBC9AsXxYw2qa6CFRutdIw2ECqGirA4K4GkwWp1s26XSECiAl7KyPzxuobGdSSQhaAF6GmEIegBhCB6AS1xZMInOdifOg80KcBbDqlTvNzUXBDvAEFHwrLPtgQNCBIATQgxghIDW1JRpVhSAA0IcgLpqBwKgu3oEBKCp2mEBCKp2eICxardm5ZOdZgMwiOF+9ZgVQN9T5NkmcanY8wKw0OT77SpaAJ+L32wU8In/czVxkev32D8tpedivsaHB+jbT2VSTNMTTgEC4wMq4J4ub0VnAAXojA+gAK3xsgDK1vnjQ+qZqG5OEwmhqJt6TI6foharAtzGs54BCeN5AIgK1JTwwQMcqy9QcHfdTPhlGg/w+v4JVq3HpxLXZmSql/8bhzrE/cJF+Xbf/zEXMgwIGgCzOcXcBYDCi5g1FgUw3qOYG70C39PtD0Aq3TXKAAAAAElFTkSuQmCC"
  11 + class="arrow" mode="aspectFit"></image>
  12 + </view>
  13 +
  14 + <view class="labels">
  15 + <view class="label-item" v-for="(label) in labels" :key="label">
  16 + <text class="t">{{label}}</text>
  17 + </view>
  18 + </view>
  19 +
  20 + <view :class="['row', isBorder ? 'border' : '']" v-for="row in rows">
  21 + <view @click="onChoose(item)" :class="[
  22 + 'day-item',
  23 + item.show ? 'show' : '',
  24 + active === item.date ? 'active' : ''
  25 + ]" v-for="(item, ind) in days.slice(row*7, (row+1)*7)" :key="item.date">
  26 + <text class="t">{{item.label}}</text>
  27 + <text v-if="item.data && item.data.text" class="data"
  28 + :style="`color: ${colors[item.data.type] || '#f3a73f'};`">{{item.data.text}}</text>
  29 + </view>
  30 +
  31 + </view>
  32 + </view>
  33 +</template>
  34 +
  35 +<script>
  36 + import calendar from './calendar'
  37 + export default {
  38 + name: "w-calendar",
  39 + emits: ['choose', 'change'],
  40 + props: {
  41 + data: {
  42 + type: Array,
  43 + default () {
  44 + return []
  45 + }
  46 + },
  47 + colors: {
  48 + type: Array,
  49 + default () {
  50 + return ['#2979ff', '#18bc37', '#f3a73f', '#e43d33', '#8f939c']
  51 + }
  52 + },
  53 + format: {
  54 + type: String,
  55 + default: ''
  56 + },
  57 + isLess: {
  58 + type: Boolean,
  59 + default: false
  60 + },
  61 + isBorder: {
  62 + type: Boolean,
  63 + default: true
  64 + },
  65 + isEn: {
  66 + type: Boolean,
  67 + default: false
  68 + }
  69 + },
  70 + computed: {
  71 + headText() {
  72 + if (this.format === '/') {
  73 + return `${this.year}/${this.month}`
  74 + } else {
  75 + if (this.isEn) {
  76 + return `${calendar.getEn(this.month)}, ${this.year}`
  77 + } else {
  78 + return `${this.year}年${this.month}月`
  79 + }
  80 + }
  81 + }
  82 + },
  83 + data() {
  84 + return {
  85 + labels: this.isEn ? calendar.labels_en : calendar.labels_zh,
  86 + active: '',
  87 + rows: [0, 1, 2, 3, 4, 5, 6],
  88 + days: [],
  89 + year: '',
  90 + month: '',
  91 +
  92 + };
  93 + },
  94 + created() {
  95 + this.refresh()
  96 + },
  97 + methods: {
  98 + refresh() {
  99 + this.$nextTick(() => {
  100 + const {days, year, month} = calendar.getDays(this.year, this.month, this.data, this.isLess)
  101 + this.days = days
  102 + this.year = year
  103 + this.month = month + 1
  104 + })
  105 + },
  106 + onChange(type) {
  107 + this.month = type === 'prev' ? (this.month - 1) : (this.month + 1)
  108 + this.refresh()
  109 + this.$nextTick(() => {
  110 + this.$emit('change', {
  111 + year: this.year,
  112 + month: this.month
  113 + })
  114 + })
  115 + },
  116 + onChoose(item) {
  117 + if (!item.show) return;
  118 + this.active = item.date
  119 + this.$nextTick(() => {
  120 + this.$emit('choose', {
  121 + date: item.date,
  122 + data: item.data
  123 + })
  124 + })
  125 + }
  126 + }
  127 + }
  128 +</script>
  129 +
  130 +<style scoped>
  131 + .arrow {
  132 + /* #ifndef APP-NVUE */
  133 + box-sizing: border-box;
  134 + /* #endif */
  135 + padding: 4px;
  136 + width: 22px;
  137 + height: 22px;
  138 + background: #f7f7f7;
  139 + border-radius: 22px;
  140 + }
  141 +
  142 + .arrow.left {
  143 + transform: rotate(180deg);
  144 + }
  145 +
  146 + .wn-calendar {
  147 + min-width: 294px;
  148 + background: white;
  149 + }
  150 +
  151 + .head {
  152 + /* #ifndef APP-NVUE */
  153 + display: flex;
  154 + /* #endif */
  155 + flex-direction: row;
  156 + justify-content: center;
  157 + align-items: center;
  158 + padding: 8px 0px;
  159 + }
  160 +
  161 + .head .t {
  162 + padding: 0 42px;
  163 + font-size: 16px;
  164 + line-height: 32px;
  165 + color: #3a3a3a;
  166 + }
  167 +
  168 + .labels {
  169 + /* #ifndef APP-NVUE */
  170 + box-sizing: border-box;
  171 + display: flex;
  172 + /* #endif */
  173 + flex-direction: row;
  174 + height: 36px;
  175 + align-items: center;
  176 + background: #f7f7f7;
  177 + border-top: 1px solid #f0f0f0;
  178 + border-bottom: 1px solid #f0f0f0;
  179 + }
  180 +
  181 + .label-item {
  182 + min-width: 42px;
  183 + flex: 1;
  184 + /* #ifndef APP-NVUE */
  185 + display: flex;
  186 + align-items: center;
  187 + justify-content: center;
  188 + /* #endif */
  189 + }
  190 +
  191 + .label-item .t {
  192 + font-size: 14px;
  193 + text-align: center;
  194 + line-height: 14px;
  195 + color: #3a3a3a;
  196 + }
  197 +
  198 + .row {
  199 + /* #ifndef APP-NVUE */
  200 + box-sizing: border-box;
  201 + display: flex;
  202 + /* #endif */
  203 + flex-direction: row;
  204 + border-bottom: 1px solid transparent;
  205 + }
  206 +
  207 + .row.border {
  208 + border-bottom: 1px solid #f0f0f0;
  209 + }
  210 +
  211 + .day-item {
  212 + /* #ifndef APP-NVUE */
  213 + box-sizing: border-box;
  214 + display: flex;
  215 + flex-direction: column;
  216 + align-items: center;
  217 + /* #endif */
  218 + flex: 1;
  219 + justify-content: center;
  220 + padding: 7px 0px;
  221 + border-radius: 4px;
  222 + height: 42px;
  223 + min-width: 42px;
  224 + }
  225 +
  226 + .day-item.show {}
  227 +
  228 + .day-item.active {
  229 + background: #ededed !important;
  230 + }
  231 +
  232 + .day-item.active .t {
  233 + font-weight: bold !important;
  234 + }
  235 +
  236 + .day-item .t {
  237 + font-size: 15px;
  238 + line-height: 15px;
  239 + text-align: center;
  240 + color: #909399;
  241 + }
  242 +
  243 + .day-item.show .t {
  244 + color: #3a3a3a;
  245 + }
  246 +
  247 + .day-item .data {
  248 + margin-top: 2px;
  249 + font-size: 11px;
  250 + line-height: 11px;
  251 + text-align: center;
  252 + /* #ifdef APP-NVUE */
  253 + text-overflow: clip;
  254 + word-wrap: anywhere;
  255 + /* #endif */
  256 + color: #3a3a3a;
  257 + }
  258 +</style>
0 \ No newline at end of file 259 \ No newline at end of file
uni_modules/wn-calendar/package.json 0 → 100644
  1 +{
  2 + "id": "wn-calendar",
  3 + "name": "wn-calendar",
  4 + "displayName": "wn-calendar 展示打卡日历",
  5 + "version": "1.0.0",
  6 + "description": "展示打卡日历组件 | 主打轻巧易改、支持填充文本多色彩、选中日期返回、中英语言",
  7 + "keywords": [
  8 + "日历",
  9 + "calendar",
  10 + "日期",
  11 + "打卡",
  12 + "打点"
  13 +],
  14 + "author": "Wenyp"
  15 +}