Compare commits
39 Commits
@ -1,28 +0,0 @@ |
||||
<template> |
||||
<!-- '<audio/>' 组件不再维护,建议使用能力更强的 'uni.createInnerAudioContext' 接口 有时间再改--> |
||||
<!--增加audio标签支持--> |
||||
<audio |
||||
:id="node.attr.id" |
||||
:class="node.classStr" |
||||
:style="node.styleStr" |
||||
:src="node.attr.src" |
||||
:loop="node.attr.loop" |
||||
:poster="node.attr.poster" |
||||
:name="node.attr.name" |
||||
:author="node.attr.author" |
||||
controls></audio> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'wxParseAudio', |
||||
props: { |
||||
node: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
}, |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
@ -1,94 +0,0 @@ |
||||
<template> |
||||
<image |
||||
:mode="node.attr.mode" |
||||
:lazy-load="node.attr.lazyLoad" |
||||
:class="node.classStr" |
||||
:style="newStyleStr || node.styleStr" |
||||
:data-src="node.attr.src" |
||||
:src="node.attr.src" |
||||
@tap="wxParseImgTap" |
||||
@load="wxParseImgLoad" |
||||
/> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'wxParseImg', |
||||
data() { |
||||
return { |
||||
newStyleStr: '', |
||||
preview: true |
||||
}; |
||||
}, |
||||
inject: ['parseWidth'], |
||||
mounted() {}, |
||||
props: { |
||||
node: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
wxParseImgTap(e) { |
||||
if (!this.preview) return; |
||||
const { src } = e.currentTarget.dataset; |
||||
if (!src) return; |
||||
let parent = this.$parent; |
||||
while (!parent.preview || typeof parent.preview !== 'function') { |
||||
// TODO 遍历获取父节点执行方法 |
||||
parent = parent.$parent; |
||||
} |
||||
parent.preview(src, e); |
||||
}, |
||||
// 图片视觉宽高计算函数区 |
||||
wxParseImgLoad(e) { |
||||
const { src } = e.currentTarget.dataset; |
||||
if (!src) return; |
||||
let { width, height } = e.mp.detail; |
||||
|
||||
const recal = this.wxAutoImageCal(width, height); |
||||
|
||||
const { imageheight, imageWidth } = recal; |
||||
const { padding, mode } = this.node.attr;//删除padding |
||||
// const { mode } = this.node.attr; |
||||
|
||||
const { styleStr } = this.node; |
||||
const imageHeightStyle = mode === 'widthFix' ? '' : `height: ${imageheight}px;`; |
||||
|
||||
this.newStyleStr = `${styleStr}; ${imageHeightStyle}; width: ${imageWidth}px; padding: 0 ${+padding}px;`;//删除padding |
||||
// this.newStyleStr = `${styleStr}; ${imageHeightStyle}; width: ${imageWidth}px;`; |
||||
}, |
||||
// 计算视觉优先的图片宽高 |
||||
wxAutoImageCal(originalWidth, originalHeight) { |
||||
// 获取图片的原始长宽 |
||||
const windowWidth = this.parseWidth.value; |
||||
const results = {}; |
||||
|
||||
if (originalWidth < 60 || originalHeight < 60) { |
||||
const { src } = this.node.attr; |
||||
let parent = this.$parent; |
||||
while (!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.removeImageUrl(src); |
||||
this.preview = false; |
||||
} |
||||
|
||||
// 判断按照那种方式进行缩放 |
||||
if (originalWidth > windowWidth) { |
||||
// 在图片width大于手机屏幕width时候 |
||||
results.imageWidth = windowWidth; |
||||
results.imageheight = windowWidth * (originalHeight / originalWidth); |
||||
} else { |
||||
// 否则展示原来的数据 |
||||
results.imageWidth = originalWidth; |
||||
results.imageheight = originalHeight; |
||||
} |
||||
return results; |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,55 +0,0 @@ |
||||
<template> |
||||
<div class='tablebox'> |
||||
<rich-text :nodes="nodes" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
export default { |
||||
name: 'wxParseTable', |
||||
props: { |
||||
node: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
}, |
||||
}, |
||||
}, |
||||
inject: ['parseSelect'], |
||||
data() { |
||||
return { |
||||
nodes:[] |
||||
}; |
||||
}, |
||||
mounted() { |
||||
this.nodes=this.loadNode([this.node]); |
||||
}, |
||||
methods: { |
||||
loadNode(node) { |
||||
let obj = []; |
||||
for (let children of node) { |
||||
if (children.node=='element') { |
||||
let t = { |
||||
name:children.tag, |
||||
attrs: { |
||||
class: children.classStr, |
||||
// style: children.styleStr, |
||||
}, |
||||
children: children.nodes?this.loadNode(children.nodes):[] |
||||
} |
||||
|
||||
obj.push(t) |
||||
} else if(children.node=='text'){ |
||||
obj.push({ |
||||
type: 'text', |
||||
text: children.text |
||||
}) |
||||
} |
||||
} |
||||
return obj |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
<style> |
||||
@import url("../parse.css"); |
||||
</style> |
@ -1,98 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node"/> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
// #ifdef APP-PLUS | H5 |
||||
import wxParseTemplate from './wxParseTemplate0'; |
||||
// #endif |
||||
// #ifdef MP |
||||
import wxParseTemplate from './wxParseTemplate1'; |
||||
// #endif |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
// #ifdef APP-PLUS | H5 |
||||
name: 'wxParseTemplate', |
||||
// #endif |
||||
// #ifdef MP |
||||
name: 'wxParseTemplate0', |
||||
// #endif |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset;// TODO currentTarget才有dataset |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') {// TODO 遍历获取父节点执行方法 |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate2'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate1', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate11'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate10', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,86 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate11', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate3'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate2', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate4'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate3', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate5'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate4', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate6'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate5', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate7'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate6', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate8'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate7', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate9'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate8', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,88 +0,0 @@ |
||||
<template> |
||||
<!--判断是否是标签节点--> |
||||
<block v-if="node.node == 'element'"> |
||||
<!--button类型--> |
||||
<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr"> |
||||
<wx-parse-template :node="node" /> |
||||
</button> |
||||
|
||||
<!--a类型--> |
||||
<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--li类型--> |
||||
<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
|
||||
<!--table类型--> |
||||
<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" /> |
||||
|
||||
<!--br类型--> |
||||
<!-- #ifndef H5 --> |
||||
<text v-else-if="node.tag == 'br'">\n</text> |
||||
<!-- #endif --> |
||||
<!-- #ifdef H5 --> |
||||
<br v-else-if="node.tag == 'br'"> |
||||
<!-- #endif --> |
||||
|
||||
<!--video类型--> |
||||
<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/> |
||||
|
||||
<!--audio类型--> |
||||
<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/> |
||||
|
||||
<!--img类型--> |
||||
<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/> |
||||
|
||||
<!--其他标签--> |
||||
<view v-else :class="node.classStr" :style="node.styleStr"> |
||||
<block v-for="(node, index) of node.nodes" :key="index"> |
||||
<wx-parse-template :node="node" /> |
||||
</block> |
||||
</view> |
||||
</block> |
||||
|
||||
<!--判断是否是文本节点--> |
||||
<block v-else-if="node.node == 'text' ">{{node.text}}</block> |
||||
</template> |
||||
|
||||
<script> |
||||
import wxParseTemplate from './wxParseTemplate10'; |
||||
import wxParseImg from './wxParseImg'; |
||||
import wxParseVideo from './wxParseVideo'; |
||||
import wxParseAudio from './wxParseAudio'; |
||||
import wxParseTable from './wxParseTable'; |
||||
|
||||
export default { |
||||
name: 'wxParseTemplate9', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
components: { |
||||
wxParseTemplate, |
||||
wxParseImg, |
||||
wxParseVideo, |
||||
wxParseAudio, |
||||
wxParseTable |
||||
}, |
||||
methods: { |
||||
wxParseATap(attr,e) { |
||||
const { |
||||
href |
||||
} = e.currentTarget.dataset; |
||||
if (!href) return; |
||||
let parent = this.$parent; |
||||
while(!parent.preview || typeof parent.preview !== 'function') { |
||||
parent = parent.$parent; |
||||
} |
||||
parent.navigate(href, e, attr); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -1,15 +0,0 @@ |
||||
<template> |
||||
<!--增加video标签支持,并循环添加--> |
||||
<view :class="node.classStr" :style="node.styleStr"> |
||||
<video :class="node.classStr" :style="node.styleStr" class="video-video" :src="node.attr.src"></video> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'wxParseVideo', |
||||
props: { |
||||
node: {}, |
||||
}, |
||||
}; |
||||
</script> |
@ -1,261 +0,0 @@ |
||||
/** |
||||
* html2Json 改造来自: https://github.com/Jxck/html2json
|
||||
* |
||||
* |
||||
* author: Di (微信小程序开发工程师) |
||||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
|
||||
* 垂直微信小程序开发交流社区 |
||||
* |
||||
* github地址: https://github.com/icindy/wxParse
|
||||
* |
||||
* for: 微信小程序富文本解析 |
||||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
|
||||
*/ |
||||
|
||||
import wxDiscode from './wxDiscode'; |
||||
import HTMLParser from './htmlparser'; |
||||
|
||||
function makeMap(str) { |
||||
const obj = {}; |
||||
const items = str.split(','); |
||||
for (let i = 0; i < items.length; i += 1) obj[items[i]] = true; |
||||
return obj; |
||||
} |
||||
|
||||
// Block Elements - HTML 5
|
||||
const block = makeMap('br,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); |
||||
|
||||
// Inline Elements - HTML 5
|
||||
const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); |
||||
|
||||
// Elements that you can, intentionally, leave open
|
||||
// (and which close themselves)
|
||||
const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); |
||||
|
||||
function removeDOCTYPE(html) { |
||||
const isDocument = /<body.*>([^]*)<\/body>/.test(html); |
||||
return isDocument ? RegExp.$1 : html; |
||||
} |
||||
|
||||
function trimHtml(html) { |
||||
return html |
||||
.replace(/<!--.*?-->/gi, '') |
||||
.replace(/\/\*.*?\*\//gi, '') |
||||
.replace(/[ ]+</gi, '<') |
||||
.replace(/<script[^]*<\/script>/gi, '') |
||||
.replace(/<style[^]*<\/style>/gi, ''); |
||||
} |
||||
|
||||
function getScreenInfo() { |
||||
const screen = {}; |
||||
wx.getSystemInfo({ |
||||
success: (res) => { |
||||
screen.width = res.windowWidth; |
||||
screen.height = res.windowHeight; |
||||
}, |
||||
}); |
||||
return screen; |
||||
} |
||||
|
||||
function html2json(html, customHandler, imageProp, host) { |
||||
// 处理字符串
|
||||
html = removeDOCTYPE(html); |
||||
html = trimHtml(html); |
||||
html = wxDiscode.strDiscode(html); |
||||
// 生成node节点
|
||||
const bufArray = []; |
||||
const results = { |
||||
nodes: [], |
||||
imageUrls: [], |
||||
}; |
||||
|
||||
const screen = getScreenInfo(); |
||||
function Node(tag) { |
||||
this.node = 'element'; |
||||
this.tag = tag; |
||||
|
||||
this.$screen = screen; |
||||
} |
||||
|
||||
HTMLParser(html, { |
||||
start(tag, attrs, unary) { |
||||
// node for this element
|
||||
const node = new Node(tag); |
||||
|
||||
if (bufArray.length !== 0) { |
||||
const parent = bufArray[0]; |
||||
if (parent.nodes === undefined) { |
||||
parent.nodes = []; |
||||
} |
||||
} |
||||
|
||||
if (block[tag]) { |
||||
node.tagType = 'block'; |
||||
} else if (inline[tag]) { |
||||
node.tagType = 'inline'; |
||||
} else if (closeSelf[tag]) { |
||||
node.tagType = 'closeSelf'; |
||||
} |
||||
|
||||
node.attr = attrs.reduce((pre, attr) => { |
||||
const { name } = attr; |
||||
let { value } = attr; |
||||
if (name === 'class') { |
||||
node.classStr = value; |
||||
} |
||||
// has multi attibutes
|
||||
// make it array of attribute
|
||||
if (name === 'style') { |
||||
node.styleStr = value; |
||||
} |
||||
if (value.match(/ /)) { |
||||
value = value.split(' '); |
||||
} |
||||
|
||||
// if attr already exists
|
||||
// merge it
|
||||
if (pre[name]) { |
||||
if (Array.isArray(pre[name])) { |
||||
// already array, push to last
|
||||
pre[name].push(value); |
||||
} else { |
||||
// single value, make it array
|
||||
pre[name] = [pre[name], value]; |
||||
} |
||||
} else { |
||||
// not exist, put it
|
||||
pre[name] = value; |
||||
} |
||||
|
||||
return pre; |
||||
}, {}); |
||||
|
||||
// 优化样式相关属性
|
||||
if (node.classStr) { |
||||
node.classStr += ` ${node.tag}`; |
||||
} else { |
||||
node.classStr = node.tag; |
||||
} |
||||
if (node.tagType === 'inline') { |
||||
node.classStr += ' inline'; |
||||
} |
||||
|
||||
// 对img添加额外数据
|
||||
if (node.tag === 'img') { |
||||
let imgUrl = node.attr.src; |
||||
imgUrl = wxDiscode.urlToHttpUrl(imgUrl, imageProp.domain); |
||||
Object.assign(node.attr, imageProp, { |
||||
src: imgUrl || '', |
||||
}); |
||||
if (imgUrl) { |
||||
results.imageUrls.push(imgUrl); |
||||
} |
||||
} |
||||
|
||||
// 处理a标签属性
|
||||
if (node.tag === 'a') { |
||||
node.attr.href = node.attr.href || ''; |
||||
} |
||||
|
||||
// 处理font标签样式属性
|
||||
if (node.tag === 'font') { |
||||
const fontSize = [ |
||||
'x-small', |
||||
'small', |
||||
'medium', |
||||
'large', |
||||
'x-large', |
||||
'xx-large', |
||||
'-webkit-xxx-large', |
||||
]; |
||||
const styleAttrs = { |
||||
color: 'color', |
||||
face: 'font-family', |
||||
size: 'font-size', |
||||
}; |
||||
if (!node.styleStr) node.styleStr = ''; |
||||
Object.keys(styleAttrs).forEach((key) => { |
||||
if (node.attr[key]) { |
||||
const value = key === 'size' ? fontSize[node.attr[key] - 1] : node.attr[key]; |
||||
node.styleStr += `${styleAttrs[key]}: ${value};`; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
// 临时记录source资源
|
||||
if (node.tag === 'source') { |
||||
results.source = node.attr.src; |
||||
} |
||||
|
||||
if (customHandler.start) { |
||||
customHandler.start(node, results); |
||||
} |
||||
|
||||
if (unary) { |
||||
// if this tag doesn't have end tag
|
||||
// like <img src="hoge.png"/>
|
||||
// add to parents
|
||||
const parent = bufArray[0] || results; |
||||
if (parent.nodes === undefined) { |
||||
parent.nodes = []; |
||||
} |
||||
parent.nodes.push(node); |
||||
} else { |
||||
bufArray.unshift(node); |
||||
} |
||||
}, |
||||
end(tag) { |
||||
// merge into parent tag
|
||||
const node = bufArray.shift(); |
||||
if (node.tag !== tag) { |
||||
console.error('invalid state: mismatch end tag'); |
||||
} |
||||
|
||||
// 当有缓存source资源时于于video补上src资源
|
||||
if (node.tag === 'video' && results.source) { |
||||
node.attr.src = results.source; |
||||
delete results.source; |
||||
} |
||||
|
||||
if (customHandler.end) { |
||||
customHandler.end(node, results); |
||||
} |
||||
|
||||
if (bufArray.length === 0) { |
||||
results.nodes.push(node); |
||||
} else { |
||||
const parent = bufArray[0]; |
||||
if (!parent.nodes) { |
||||
parent.nodes = []; |
||||
} |
||||
parent.nodes.push(node); |
||||
} |
||||
}, |
||||
chars(text) { |
||||
if (!text.trim()) return; |
||||
|
||||
const node = { |
||||
node: 'text', |
||||
text, |
||||
}; |
||||
|
||||
if (customHandler.chars) { |
||||
customHandler.chars(node, results); |
||||
} |
||||
|
||||
if (bufArray.length === 0) { |
||||
results.nodes.push(node); |
||||
} else { |
||||
const parent = bufArray[0]; |
||||
if (parent.nodes === undefined) { |
||||
parent.nodes = []; |
||||
} |
||||
parent.nodes.push(node); |
||||
} |
||||
}, |
||||
}); |
||||
|
||||
return results; |
||||
} |
||||
|
||||
export default html2json; |
@ -1,156 +0,0 @@ |
||||
/** |
||||
* |
||||
* htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
|
||||
* |
||||
* author: Di (微信小程序开发工程师) |
||||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
|
||||
* 垂直微信小程序开发交流社区 |
||||
* |
||||
* github地址: https://github.com/icindy/wxParse
|
||||
* |
||||
* for: 微信小程序富文本解析 |
||||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
|
||||
*/ |
||||
// Regular Expressions for parsing tags and attributes
|
||||
|
||||
const startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z0-9_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; |
||||
const endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/; |
||||
const attr = /([a-zA-Z0-9_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; |
||||
|
||||
function makeMap(str) { |
||||
const obj = {}; |
||||
const items = str.split(','); |
||||
for (let i = 0; i < items.length; i += 1) obj[items[i]] = true; |
||||
return obj; |
||||
} |
||||
|
||||
// Empty Elements - HTML 5
|
||||
const empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); |
||||
|
||||
// Block Elements - HTML 5
|
||||
const block = makeMap('address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); |
||||
|
||||
// Inline Elements - HTML 5
|
||||
const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); |
||||
|
||||
// Elements that you can, intentionally, leave open
|
||||
// (and which close themselves)
|
||||
const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); |
||||
|
||||
// Attributes that have their values filled in disabled="disabled"
|
||||
const fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); |
||||
|
||||
function HTMLParser(html, handler) { |
||||
let index; |
||||
let chars; |
||||
let match; |
||||
let last = html; |
||||
const stack = []; |
||||
|
||||
stack.last = () => stack[stack.length - 1]; |
||||
|
||||
function parseEndTag(tag, tagName) { |
||||
// If no tag name is provided, clean shop
|
||||
let pos; |
||||
if (!tagName) { |
||||
pos = 0; |
||||
} else { |
||||
// Find the closest opened tag of the same type
|
||||
tagName = tagName.toLowerCase(); |
||||
for (pos = stack.length - 1; pos >= 0; pos -= 1) { |
||||
if (stack[pos] === tagName) break; |
||||
} |
||||
} |
||||
if (pos >= 0) { |
||||
// Close all the open elements, up the stack
|
||||
for (let i = stack.length - 1; i >= pos; i -= 1) { |
||||
if (handler.end) handler.end(stack[i]); |
||||
} |
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos; |
||||
} |
||||
} |
||||
|
||||
function parseStartTag(tag, tagName, rest, unary) { |
||||
tagName = tagName.toLowerCase(); |
||||
|
||||
if (block[tagName]) { |
||||
while (stack.last() && inline[stack.last()]) { |
||||
parseEndTag('', stack.last()); |
||||
} |
||||
} |
||||
|
||||
if (closeSelf[tagName] && stack.last() === tagName) { |
||||
parseEndTag('', tagName); |
||||
} |
||||
|
||||
unary = empty[tagName] || !!unary; |
||||
|
||||
if (!unary) stack.push(tagName); |
||||
|
||||
if (handler.start) { |
||||
const attrs = []; |
||||
|
||||
rest.replace(attr, function genAttr(matches, name) { |
||||
const value = arguments[2] || arguments[3] || arguments[4] || (fillAttrs[name] ? name : ''); |
||||
|
||||
attrs.push({ |
||||
name, |
||||
value, |
||||
escaped: value.replace(/(^|[^\\])"/g, '$1\\"'), // "
|
||||
}); |
||||
}); |
||||
|
||||
if (handler.start) { |
||||
handler.start(tagName, attrs, unary); |
||||
} |
||||
} |
||||
} |
||||
|
||||
while (html) { |
||||
chars = true; |
||||
|
||||
if (html.indexOf('</') === 0) { |
||||
match = html.match(endTag); |
||||
|
||||
if (match) { |
||||
html = html.substring(match[0].length); |
||||
match[0].replace(endTag, parseEndTag); |
||||
chars = false; |
||||
} |
||||
|
||||
// start tag
|
||||
} else if (html.indexOf('<') === 0) { |
||||
match = html.match(startTag); |
||||
|
||||
if (match) { |
||||
html = html.substring(match[0].length); |
||||
match[0].replace(startTag, parseStartTag); |
||||
chars = false; |
||||
} |
||||
} |
||||
|
||||
if (chars) { |
||||
index = html.indexOf('<'); |
||||
let text = ''; |
||||
while (index === 0) { |
||||
text += '<'; |
||||
html = html.substring(1); |
||||
index = html.indexOf('<'); |
||||
} |
||||
text += index < 0 ? html : html.substring(0, index); |
||||
html = index < 0 ? '' : html.substring(index); |
||||
|
||||
if (handler.chars) handler.chars(text); |
||||
} |
||||
|
||||
if (html === last) throw new Error(`Parse Error: ${html}`); |
||||
last = html; |
||||
} |
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag(); |
||||
} |
||||
|
||||
export default HTMLParser; |
@ -1,258 +0,0 @@ |
||||
/** |
||||
* author: Di (微信小程序开发工程师) |
||||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) |
||||
* 垂直微信小程序开发交流社区 |
||||
* |
||||
* github地址: https://github.com/icindy/wxParse |
||||
* |
||||
* for: 微信小程序富文本解析 |
||||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 |
||||
*/ |
||||
/** |
||||
* 请在全局下引入该文件,@import '/static/wxParse.css'; |
||||
*/ |
||||
.wxParse { |
||||
user-select:none; |
||||
width: 100%; |
||||
font-family: Helvetica, "PingFangSC", 'Microsoft Yahei', '微软雅黑', Arial, sans-serif; |
||||
color: #333; |
||||
line-height: 1.5; |
||||
font-size: 1em; |
||||
text-align:justify;/* //左右两端对齐 */ |
||||
} |
||||
.wxParse view ,.wxParse uni-view{ |
||||
word-break: break-word; |
||||
} |
||||
.wxParse .p { |
||||
padding-bottom: 0.5em; |
||||
clear: both; |
||||
/* letter-spacing: 0;//字间距 */ |
||||
} |
||||
.wxParse .inline { |
||||
display: inline; |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
.wxParse .div { |
||||
margin: 0; |
||||
padding: 0; |
||||
display: block; |
||||
} |
||||
|
||||
.wxParse .h1{ |
||||
font-size: 2em; |
||||
line-height: 1.2em; |
||||
margin: 0.67em 0; |
||||
} |
||||
.wxParse .h2{ |
||||
font-size: 1.5em; |
||||
margin: 0.83em 0; |
||||
} |
||||
.wxParse .h3{ |
||||
font-size: 1.17em; |
||||
margin: 1em 0; |
||||
} |
||||
.wxParse .h4{ |
||||
margin: 1.33em 0; |
||||
} |
||||
.wxParse .h5{ |
||||
font-size: 0.83em; |
||||
margin: 1.67em 0; |
||||
} |
||||
.wxParse .h6{ |
||||
font-size: 0.83em; |
||||
margin: 1.67em 0; |
||||
} |
||||
|
||||
.wxParse .h1, |
||||
.wxParse .h2, |
||||
.wxParse .h3, |
||||
.wxParse .h4, |
||||
.wxParse .h5, |
||||
.wxParse .h6, |
||||
.wxParse .b, |
||||
.wxParse .strong{ |
||||
font-weight: bolder; |
||||
} |
||||
|
||||
.wxParse .i, |
||||
.wxParse .cite, |
||||
.wxParse .em, |
||||
.wxParse .var, |
||||
.wxParse .address { |
||||
font-style: italic; |
||||
} |
||||
.wxParse .spaceshow{ |
||||
white-space: pre; |
||||
} |
||||
.wxParse .pre, |
||||
.wxParse .tt, |
||||
.wxParse .code, |
||||
.wxParse .kbd, |
||||
.wxParse .samp { |
||||
font-family: monospace; |
||||
} |
||||
.wxParse .pre { |
||||
overflow: auto; |
||||
background: #f5f5f5; |
||||
padding: 16upx; |
||||
white-space: pre; |
||||
margin: 1em 0upx; |
||||
font-size: 24upx; |
||||
} |
||||
.wxParse .code { |
||||
overflow: auto; |
||||
padding: 16upx; |
||||
white-space: pre; |
||||
margin: 1em 0upx; |
||||
background: #f5f5f5; |
||||
font-size: 24upx; |
||||
} |
||||
|
||||
.wxParse .big { |
||||
font-size: 1.17em; |
||||
} |
||||
|
||||
.wxParse .small, |
||||
.wxParse .sub, |
||||
.wxParse .sup { |
||||
font-size: 0.83em; |
||||
} |
||||
|
||||
.wxParse .sub { |
||||
vertical-align: sub; |
||||
} |
||||
.wxParse .sup { |
||||
vertical-align: super; |
||||
} |
||||
|
||||
.wxParse .s, |
||||
.wxParse .strike, |
||||
.wxParse .del { |
||||
text-decoration: line-through; |
||||
} |
||||
|
||||
.wxParse .strong, |
||||
.wxParse .text, |
||||
.wxParse .span, |
||||
.wxParse .s { |
||||
display: inline; |
||||
} |
||||
|
||||
.wxParse .a { |
||||
color: deepskyblue; |
||||
} |
||||
|
||||
.wxParse .video { |
||||
text-align: center; |
||||
margin: 22upx 0; |
||||
} |
||||
|
||||
.wxParse .video-video { |
||||
width: 100%; |
||||
} |
||||
.wxParse .uni-image{ |
||||
max-width: 100%; |
||||
} |
||||
.wxParse .img { |
||||
display: block; |
||||
max-width: 100%; |
||||
margin-bottom: 0em;/* //与p标签底部padding同时修改 */ |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.wxParse .blockquote { |
||||
margin: 10upx 0; |
||||
padding: 22upx 0 22upx 22upx; |
||||
font-family: Courier, Calibri, "宋体"; |
||||
background: #f5f5f5; |
||||
border-left: 6upx solid #dbdbdb; |
||||
} |
||||
.wxParse .blockquote .p { |
||||
margin: 0; |
||||
} |
||||
.wxParse .ul, .wxParse .ol { |
||||
display: block; |
||||
margin: 1em 0; |
||||
padding-left: 2em; |
||||
} |
||||
.wxParse .ol { |
||||
list-style-type: disc; |
||||
} |
||||
.wxParse .ol { |
||||
list-style-type: decimal; |
||||
} |
||||
.wxParse .ol>weixin-parse-template,.wxParse .ul>weixin-parse-template { |
||||
display: list-item; |
||||
align-items: baseline; |
||||
text-align: match-parent; |
||||
} |
||||
|
||||
.wxParse .ol>.li,.wxParse .ul>.li { |
||||
display: list-item; |
||||
align-items: baseline; |
||||
text-align: match-parent; |
||||
} |
||||
.wxParse .ul .ul, .wxParse .ol .ul { |
||||
list-style-type: circle; |
||||
} |
||||
.wxParse .ol .ol .ul, .wxParse .ol .ul .ul, .wxParse .ul .ol .ul, .wxParse .ul .ul .ul { |
||||
list-style-type: square; |
||||
} |
||||
|
||||
.wxParse .u { |
||||
text-decoration: underline; |
||||
} |
||||
.wxParse .hide { |
||||
display: none; |
||||
} |
||||
.wxParse .del { |
||||
display: inline; |
||||
} |
||||
.wxParse .figure { |
||||
overflow: hidden; |
||||
} |
||||
.wxParse .tablebox{ |
||||
overflow: auto; |
||||
background-color: #f5f5f5; |
||||
background: #f5f5f5; |
||||
font-size: 13px; |
||||
padding: 8px; |
||||
} |
||||
.wxParse .table .table,.wxParse .table{ |
||||
border-collapse:collapse; |
||||
box-sizing: border-box; |
||||
/* 内边框 */ |
||||
/* width: 100%; */ |
||||
overflow: auto; |
||||
white-space: pre; |
||||
} |
||||
.wxParse .tbody{ |
||||
border-collapse:collapse; |
||||
box-sizing: border-box; |
||||
/* 内边框 */ |
||||
border: 1px solid #dadada; |
||||
} |
||||
.wxParse .table .thead, .wxParse .table .tfoot, .wxParse .table .th{ |
||||
border-collapse:collapse; |
||||
box-sizing: border-box; |
||||
background: #ececec; |
||||
font-weight: 40; |
||||
} |
||||
.wxParse .table .tr { |
||||
border-collapse:collapse; |
||||
box-sizing: border-box; |
||||
/* border: 2px solid #F0AD4E; */ |
||||
overflow:auto; |
||||
} |
||||
.wxParse .table .th, |
||||
.wxParse .table .td{ |
||||
border-collapse:collapse; |
||||
box-sizing: border-box; |
||||
border: 2upx solid #dadada; |
||||
overflow:auto; |
||||
} |
||||
.wxParse .audio, .wxParse .uni-audio-default{ |
||||
display: block; |
||||
} |
@ -1,228 +0,0 @@ |
||||
<!--** |
||||
* forked from:https://github.com/F-loat/mpvue-wxParse |
||||
* |
||||
* github地址: https://github.com/dcloudio/uParse |
||||
* |
||||
* for: uni-app框架下 富文本解析 |
||||
* |
||||
* 优化 by gaoyia@qq.com https://github.com/gaoyia/parse |
||||
*/--> |
||||
|
||||
<template> |
||||
|
||||
<!--基础元素--> |
||||
<div class="wxParse" :class="className" :style="'user-select:' + userSelect"> |
||||
<block v-for="(node, index) of nodes" :key="index" v-if="!loading"> |
||||
<wxParseTemplate :node="node" /> |
||||
</block> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import HtmlToJson from './libs/html2json'; |
||||
import wxParseTemplate from './components/wxParseTemplate0'; |
||||
|
||||
|
||||
export default { |
||||
name: 'wxParse', |
||||
props: { |
||||
// user-select:none; |
||||
userSelect: { |
||||
type: String, |
||||
default: 'text' //none |text| all | element |
||||
}, |
||||
imgOptions: { |
||||
type: [Object, Boolean], |
||||
default: function() { |
||||
return { |
||||
loop: false, |
||||
indicator: 'number', |
||||
longPressActions: false |
||||
// longPressActions: { |
||||
// itemList: ['发送给朋友', '保存图片', '收藏'], |
||||
// success: function (res) { |
||||
// console.log('选中了第' + (res.tapIndex + 1) + '个按钮'); |
||||
// }, |
||||
// fail: function (res) { |
||||
// console.log(res.errMsg); |
||||
// } |
||||
// } |
||||
// } |
||||
} |
||||
} |
||||
}, |
||||
loading: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
className: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
content: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
noData: { |
||||
type: String, |
||||
default: '<div style="color: red;">数据不能为空</div>' |
||||
}, |
||||
startHandler: { |
||||
type: Function, |
||||
default () { |
||||
return node => { |
||||
node.attr.class = null; |
||||
node.attr.style = null; |
||||
}; |
||||
} |
||||
}, |
||||
endHandler: { |
||||
type: Function, |
||||
default: null |
||||
}, |
||||
charsHandler: { |
||||
type: Function, |
||||
default: null |
||||
}, |
||||
imageProp: { |
||||
type: Object, |
||||
default () { |
||||
return { |
||||
mode: 'aspectFit', |
||||
padding: 0, |
||||
lazyLoad: false, |
||||
domain: '' |
||||
}; |
||||
} |
||||
} |
||||
}, |
||||
components: { |
||||
wxParseTemplate |
||||
}, |
||||
data() { |
||||
return { |
||||
nodes: {}, |
||||
imageUrls: [], |
||||
wxParseWidth: { |
||||
value: 0 |
||||
} |
||||
}; |
||||
}, |
||||
computed: {}, |
||||
mounted() { |
||||
this.setHtml() |
||||
}, |
||||
methods: { |
||||
setHtml() { |
||||
this.getWidth().then((data) => { |
||||
this.wxParseWidth.value = data; |
||||
}) |
||||
let { |
||||
content, |
||||
noData, |
||||
imageProp, |
||||
startHandler, |
||||
endHandler, |
||||
charsHandler |
||||
} = this; |
||||
let parseData = content || noData; |
||||
let customHandler = { |
||||
start: startHandler, |
||||
end: endHandler, |
||||
chars: charsHandler |
||||
}; |
||||
let results = HtmlToJson(parseData, customHandler, imageProp, this); |
||||
|
||||
this.imageUrls = results.imageUrls; |
||||
// this.nodes = results.nodes; |
||||
|
||||
|
||||
this.nodes = []; |
||||
results.nodes.forEach((item) => { |
||||
setTimeout(() => { |
||||
this.nodes.push(item) |
||||
}, 0); |
||||
}) |
||||
}, |
||||
getWidth() { |
||||
return new Promise((res, rej) => { |
||||
// #ifndef MP-ALIPAY || MP-BAIDU |
||||
uni.createSelectorQuery() |
||||
.in(this) |
||||
.select('.wxParse') |
||||
.fields({ |
||||
size: true, |
||||
scrollOffset: true |
||||
}, |
||||
data => { |
||||
res(data.width); |
||||
} |
||||
).exec(); |
||||
// #endif |
||||
// #ifdef MP-BAIDU |
||||
const query = swan.createSelectorQuery(); |
||||
query.select('.wxParse').boundingClientRect(); |
||||
query.exec(obj => { |
||||
const rect = obj[0] |
||||
if (rect) { |
||||
res(rect.width); |
||||
} |
||||
}); |
||||
// #endif |
||||
// #ifdef MP-ALIPAY |
||||
my.createSelectorQuery() |
||||
.select('.wxParse') |
||||
.boundingClientRect().exec((ret) => { |
||||
res(ret[0].width); |
||||
}); |
||||
// #endif |
||||
}); |
||||
}, |
||||
navigate(href, $event, attr) { |
||||
console.log(href, attr); |
||||
this.$emit('navigate', href, $event); |
||||
}, |
||||
preview(src, $event) { |
||||
if (!this.imageUrls.length || typeof this.imgOptions === 'boolean') { |
||||
|
||||
} else { |
||||
uni.previewImage({ |
||||
current: src, |
||||
urls: this.imageUrls, |
||||
loop: this.imgOptions.loop, |
||||
indicator: this.imgOptions.indicator, |
||||
longPressActions: this.imgOptions.longPressActions |
||||
}); |
||||
} |
||||
this.$emit('preview', src, $event); |
||||
}, |
||||
removeImageUrl(src) { |
||||
const { |
||||
imageUrls |
||||
} = this; |
||||
imageUrls.splice(imageUrls.indexOf(src), 1); |
||||
} |
||||
}, |
||||
// 父组件中提供 |
||||
provide() { |
||||
return { |
||||
parseWidth: this.wxParseWidth, |
||||
parseSelect: this.userSelect |
||||
// 提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。 |
||||
}; |
||||
}, |
||||
watch: { |
||||
content(){ |
||||
this.setHtml() |
||||
} |
||||
// content: { |
||||
// handler: function(newVal, oldVal) { |
||||
// if (newVal !== oldVal) { |
||||
// |
||||
// } |
||||
// }, |
||||
// deep: true |
||||
// } |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,26 @@ |
||||
<template> |
||||
<view class="per-mask"> |
||||
<view class="mask"></view> |
||||
<view class="texts"> |
||||
<view class="text">权限审核中,可联系下方平台运营加快审核进度</view> |
||||
<image class="qrcode" :src="src" mode="widthFix"></image> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
src: uni.getSystemInfoSync().uniPlatform === 'mp-toutiao' ? 'https://occupationlab.com/images/dyQrcode.jpg' : 'https://occupationlab.com/images/customer.png' |
||||
} |
||||
}, |
||||
methods: { |
||||
|
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
|
||||
</style> |
@ -1,140 +0,0 @@ |
||||
<template> |
||||
<view class="uni-section"> |
||||
<view class="uni-section-header" nvue> |
||||
<view v-if="type" class="uni-section__head"> |
||||
<view :class="type" class="uni-section__head-tag"/> |
||||
</view> |
||||
<view class="uni-section__content"> |
||||
<text :class="{'distraction':!subTitle}" :style="{color:color}" class="uni-section__content-title">{{ title }}</text> |
||||
<text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text> |
||||
</view> |
||||
</view> |
||||
<view :style="{padding: padding ? '10px' : ''}"> |
||||
<slot/> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
/** |
||||
* Section 标题栏 |
||||
* @description 标题栏 |
||||
* @property {String} type = [line|circle] 标题装饰类型 |
||||
* @value line 竖线 |
||||
* @value circle 圆形 |
||||
* @property {String} title 主标题 |
||||
* @property {String} subTitle 副标题 |
||||
*/ |
||||
|
||||
export default { |
||||
name: 'UniSection', |
||||
emits:['click'], |
||||
props: { |
||||
type: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
title: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
color:{ |
||||
type: String, |
||||
default: '#333' |
||||
}, |
||||
subTitle: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
padding: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
data() { |
||||
return {} |
||||
}, |
||||
watch: { |
||||
title(newVal) { |
||||
if (uni.report && newVal !== '') { |
||||
uni.report('title', newVal) |
||||
} |
||||
} |
||||
}, |
||||
methods: { |
||||
onClick() { |
||||
this.$emit('click') |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
<style lang="scss" > |
||||
$uni-primary: #2979ff !default; |
||||
|
||||
.uni-section { |
||||
background-color: #fff; |
||||
// overflow: hidden; |
||||
margin-top: 10px; |
||||
} |
||||
.uni-section-header { |
||||
position: relative; |
||||
/* #ifndef APP-NVUE */ |
||||
display: flex; |
||||
/* #endif */ |
||||
flex-direction: row; |
||||
align-items: center; |
||||
padding: 12px 10px; |
||||
// height: 50px; |
||||
font-weight: normal; |
||||
} |
||||
.uni-section__head { |
||||
flex-direction: row; |
||||
justify-content: center; |
||||
align-items: center; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.line { |
||||
height: 12px; |
||||
background-color: $uni-primary; |
||||
border-radius: 10px; |
||||
width: 4px; |
||||
} |
||||
|
||||
.circle { |
||||
width: 8px; |
||||
height: 8px; |
||||
border-top-right-radius: 50px; |
||||
border-top-left-radius: 50px; |
||||
border-bottom-left-radius: 50px; |
||||
border-bottom-right-radius: 50px; |
||||
background-color: $uni-primary; |
||||
} |
||||
|
||||
.uni-section__content { |
||||
/* #ifndef APP-NVUE */ |
||||
display: flex; |
||||
/* #endif */ |
||||
flex-direction: column; |
||||
flex: 1; |
||||
color: #333; |
||||
} |
||||
|
||||
.uni-section__content-title { |
||||
font-size: 14px; |
||||
color: $uni-primary; |
||||
} |
||||
|
||||
.distraction { |
||||
flex-direction: row; |
||||
align-items: center; |
||||
} |
||||
|
||||
.uni-section__content-sub { |
||||
font-size: 12px; |
||||
color: #999; |
||||
line-height: 16px; |
||||
margin-top: 2px; |
||||
} |
||||
</style> |
@ -1,16 +0,0 @@ |
||||
/** |
||||
* @description 鉴权指令 |
||||
* 当传入的权限当前用户没有时,会移除该组件 |
||||
* 用例:<Tag v-auth>text</Tag> 或者:<Tag v-auth="'user:编辑'">text</Tag> |
||||
* */ |
||||
export default { |
||||
inserted(el, binding, vnode) { |
||||
const btnText = el.innerText |
||||
const btnPermissions = uni.getStorageSync('auth') |
||||
if (btnText && btnPermissions && btnPermissions.length) { |
||||
const isPermission = btnPermissions.includes(btnText) |
||||
// 如果按钮集合里没有该权限,就把该按钮给去除
|
||||
!isPermission && el.parentNode && el.parentNode.removeChild(el) |
||||
} |
||||
}, |
||||
}; |
@ -1,12 +0,0 @@ |
||||
/** |
||||
* 插件 |
||||
* */ |
||||
|
||||
import auth from './auth' |
||||
|
||||
export default { |
||||
async install(Vue, options) { |
||||
// 指令
|
||||
Vue.directive('auth', auth) |
||||
} |
||||
}; |
@ -0,0 +1,135 @@ |
||||
export const Base64 = { |
||||
|
||||
// private property
|
||||
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", |
||||
|
||||
// public method for encoding
|
||||
encode : function (input) { |
||||
var output = ""; |
||||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4; |
||||
var i = 0; |
||||
|
||||
input = Base64._utf8_encode(input); |
||||
|
||||
while (i < input.length) { |
||||
|
||||
chr1 = input.charCodeAt(i++); |
||||
chr2 = input.charCodeAt(i++); |
||||
chr3 = input.charCodeAt(i++); |
||||
|
||||
enc1 = chr1 >> 2; |
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); |
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); |
||||
enc4 = chr3 & 63; |
||||
|
||||
if (isNaN(chr2)) { |
||||
enc3 = enc4 = 64; |
||||
} else if (isNaN(chr3)) { |
||||
enc4 = 64; |
||||
} |
||||
|
||||
output = output + |
||||
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + |
||||
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); |
||||
|
||||
} |
||||
|
||||
return output; |
||||
}, |
||||
|
||||
// public method for decoding
|
||||
decode : function (input) { |
||||
var output = ""; |
||||
var chr1, chr2, chr3; |
||||
var enc1, enc2, enc3, enc4; |
||||
var i = 0; |
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); |
||||
|
||||
while (i < input.length) { |
||||
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++)); |
||||
enc2 = this._keyStr.indexOf(input.charAt(i++)); |
||||
enc3 = this._keyStr.indexOf(input.charAt(i++)); |
||||
enc4 = this._keyStr.indexOf(input.charAt(i++)); |
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4); |
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); |
||||
chr3 = ((enc3 & 3) << 6) | enc4; |
||||
|
||||
output = output + String.fromCharCode(chr1); |
||||
|
||||
if (enc3 != 64) { |
||||
output = output + String.fromCharCode(chr2); |
||||
} |
||||
if (enc4 != 64) { |
||||
output = output + String.fromCharCode(chr3); |
||||
} |
||||
|
||||
} |
||||
|
||||
output = Base64._utf8_decode(output); |
||||
|
||||
return output; |
||||
|
||||
}, |
||||
|
||||
// private method for UTF-8 encoding
|
||||
_utf8_encode : function (string) { |
||||
string = string.replace(/\r\n/g,"\n"); |
||||
var utftext = ""; |
||||
|
||||
for (var n = 0; n < string.length; n++) { |
||||
|
||||
var c = string.charCodeAt(n); |
||||
|
||||
if (c < 128) { |
||||
utftext += String.fromCharCode(c); |
||||
} |
||||
else if((c > 127) && (c < 2048)) { |
||||
utftext += String.fromCharCode((c >> 6) | 192); |
||||
utftext += String.fromCharCode((c & 63) | 128); |
||||
} |
||||
else { |
||||
utftext += String.fromCharCode((c >> 12) | 224); |
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128); |
||||
utftext += String.fromCharCode((c & 63) | 128); |
||||
} |
||||
|
||||
} |
||||
|
||||
return utftext; |
||||
}, |
||||
|
||||
// private method for UTF-8 decoding
|
||||
_utf8_decode : function (utftext) { |
||||
var string = ""; |
||||
var i = 0; |
||||
var c = c1 = c2 = 0; |
||||
|
||||
while ( i < utftext.length ) { |
||||
|
||||
c = utftext.charCodeAt(i); |
||||
|
||||
if (c < 128) { |
||||
string += String.fromCharCode(c); |
||||
i++; |
||||
} |
||||
else if((c > 191) && (c < 224)) { |
||||
c2 = utftext.charCodeAt(i+1); |
||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); |
||||
i += 2; |
||||
} |
||||
else { |
||||
c2 = utftext.charCodeAt(i+1); |
||||
c3 = utftext.charCodeAt(i+2); |
||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); |
||||
i += 3; |
||||
} |
||||
|
||||
} |
||||
|
||||
return string; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,117 @@ |
||||
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
||||
let Crypto = {}; |
||||
var util = Crypto.util = { |
||||
rotl: function (n, b) { |
||||
return (n << b) | (n >>> (32 - b)); |
||||
}, |
||||
rotr: function (n, b) { |
||||
return (n << (32 - b)) | (n >>> b); |
||||
}, |
||||
endian: function (n) { |
||||
if (n.constructor == Number) { |
||||
return util.rotl(n, 8) & 0x00FF00FF | |
||||
util.rotl(n, 24) & 0xFF00FF00; |
||||
} |
||||
for (var i = 0; i < n.length; i++) |
||||
n[i] = util.endian(n[i]); |
||||
return n; |
||||
}, |
||||
randomBytes: function (n) { |
||||
for (var bytes = []; n > 0; n--) |
||||
bytes.push(Math.floor(Math.random() * 256)); |
||||
return bytes; |
||||
}, |
||||
stringToBytes: function (str) { |
||||
var bytes = []; |
||||
for (var i = 0; i < str.length; i++) |
||||
bytes.push(str.charCodeAt(i)); |
||||
return bytes; |
||||
}, |
||||
bytesToString: function (bytes) { |
||||
var str = []; |
||||
for (var i = 0; i < bytes.length; i++) |
||||
str.push(String.fromCharCode(bytes[i])); |
||||
return str.join(""); |
||||
}, |
||||
stringToWords: function (str) { |
||||
var words = []; |
||||
for (var c = 0, b = 0; c < str.length; c++, b += 8) |
||||
words[b >>> 5] |= str.charCodeAt(c) << (24 - b % 32); |
||||
return words; |
||||
}, |
||||
bytesToWords: function (bytes) { |
||||
var words = []; |
||||
for (var i = 0, b = 0; i < bytes.length; i++, b += 8) |
||||
words[b >>> 5] |= bytes[i] << (24 - b % 32); |
||||
return words; |
||||
}, |
||||
wordsToBytes: function (words) { |
||||
var bytes = []; |
||||
for (var b = 0; b < words.length * 32; b += 8) |
||||
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); |
||||
return bytes; |
||||
}, |
||||
bytesToHex: function (bytes) { |
||||
var hex = []; |
||||
for (var i = 0; i < bytes.length; i++) { |
||||
hex.push((bytes[i] >>> 4).toString(16)); |
||||
hex.push((bytes[i] & 0xF).toString(16)); |
||||
} |
||||
return hex.join(""); |
||||
}, |
||||
hexToBytes: function (hex) { |
||||
var bytes = []; |
||||
for (var c = 0; c < hex.length; c += 2) |
||||
bytes.push(parseInt(hex.substr(c, 2), 16)); |
||||
return bytes; |
||||
}, |
||||
bytesToBase64: function (bytes) { |
||||
if (typeof btoa == "function") return btoa(util.bytesToString(bytes)); |
||||
var base64 = [], |
||||
overflow; |
||||
for (var i = 0; i < bytes.length; i++) { |
||||
switch (i % 3) { |
||||
case 0: |
||||
base64.push(base64map.charAt(bytes[i] >>> 2)); |
||||
overflow = (bytes[i] & 0x3) << 4; |
||||
break; |
||||
case 1: |
||||
base64.push(base64map.charAt(overflow | (bytes[i] >>> 4))); |
||||
overflow = (bytes[i] & 0xF) << 2; |
||||
break; |
||||
case 2: |
||||
base64.push(base64map.charAt(overflow | (bytes[i] >>> 6))); |
||||
base64.push(base64map.charAt(bytes[i] & 0x3F)); |
||||
overflow = -1; |
||||
} |
||||
} |
||||
if (overflow != undefined && overflow != -1) |
||||
base64.push(base64map.charAt(overflow)); |
||||
while (base64.length % 4 != 0) base64.push("="); |
||||
return base64.join(""); |
||||
}, |
||||
base64ToBytes: function (base64) { |
||||
if (typeof atob == "function") return util.stringToBytes(atob(base64)); |
||||
base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); |
||||
var bytes = []; |
||||
for (var i = 0; i < base64.length; i++) { |
||||
switch (i % 4) { |
||||
case 1: |
||||
bytes.push((base64map.indexOf(base64.charAt(i - 1)) << 2) | |
||||
(base64map.indexOf(base64.charAt(i)) >>> 4)); |
||||
break; |
||||
case 2: |
||||
bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0xF) << 4) | |
||||
(base64map.indexOf(base64.charAt(i)) >>> 2)); |
||||
break; |
||||
case 3: |
||||
bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0x3) << 6) | |
||||
(base64map.indexOf(base64.charAt(i)))); |
||||
break; |
||||
} |
||||
} |
||||
return bytes; |
||||
} |
||||
}; |
||||
Crypto.mode = {}; |
||||
export default Crypto; |
@ -0,0 +1,37 @@ |
||||
import Crypto from './crypto.js'; |
||||
/*! |
||||
* Crypto-JS v1.1.0 |
||||
* http://code.google.com/p/crypto-js/
|
||||
* Copyright (c) 2009, Jeff Mott. All rights reserved. |
||||
* http://code.google.com/p/crypto-js/wiki/License
|
||||
*/ |
||||
(function(){ |
||||
|
||||
// Shortcut
|
||||
var util = Crypto.util; |
||||
|
||||
Crypto.HMAC = function (hasher, message, key, options) { |
||||
|
||||
// Allow arbitrary length keys
|
||||
key = key.length > hasher._blocksize * 4 ? |
||||
hasher(key, { asBytes: true }) : |
||||
util.stringToBytes(key); |
||||
|
||||
// XOR keys with pad constants
|
||||
var okey = key, |
||||
ikey = key.slice(0); |
||||
for (var i = 0; i < hasher._blocksize * 4; i++) { |
||||
okey[i] ^= 0x5C; |
||||
ikey[i] ^= 0x36; |
||||
} |
||||
|
||||
var hmacbytes = hasher(util.bytesToString(okey) + |
||||
hasher(util.bytesToString(ikey) + message, { asString: true }), |
||||
{ asBytes: true }); |
||||
return options && options.asBytes ? hmacbytes : |
||||
options && options.asString ? util.bytesToString(hmacbytes) : |
||||
util.bytesToHex(hmacbytes); |
||||
|
||||
}; |
||||
|
||||
})(); |
@ -0,0 +1,82 @@ |
||||
import Crypto from './crypto.js'; |
||||
/*! |
||||
* Crypto-JS v1.1.0 |
||||
* http://code.google.com/p/crypto-js/
|
||||
* Copyright (c) 2009, Jeff Mott. All rights reserved. |
||||
* http://code.google.com/p/crypto-js/wiki/License
|
||||
*/ |
||||
(function(){ |
||||
|
||||
// Shortcut
|
||||
var util = Crypto.util; |
||||
|
||||
// Public API
|
||||
var SHA1 = Crypto.SHA1 = function (message, options) { |
||||
var digestbytes = util.wordsToBytes(SHA1._sha1(message)); |
||||
return options && options.asBytes ? digestbytes : |
||||
options && options.asString ? util.bytesToString(digestbytes) : |
||||
util.bytesToHex(digestbytes); |
||||
}; |
||||
|
||||
// The core
|
||||
SHA1._sha1 = function (message) { |
||||
|
||||
var m = util.stringToWords(message), |
||||
l = message.length * 8, |
||||
w = [], |
||||
H0 = 1732584193, |
||||
H1 = -271733879, |
||||
H2 = -1732584194, |
||||
H3 = 271733878, |
||||
H4 = -1009589776; |
||||
|
||||
// Padding
|
||||
m[l >> 5] |= 0x80 << (24 - l % 32); |
||||
m[((l + 64 >>> 9) << 4) + 15] = l; |
||||
|
||||
for (var i = 0; i < m.length; i += 16) { |
||||
|
||||
var a = H0, |
||||
b = H1, |
||||
c = H2, |
||||
d = H3, |
||||
e = H4; |
||||
|
||||
for (var j = 0; j < 80; j++) { |
||||
|
||||
if (j < 16) w[j] = m[i + j]; |
||||
else { |
||||
var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16]; |
||||
w[j] = (n << 1) | (n >>> 31); |
||||
} |
||||
|
||||
var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + ( |
||||
j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 : |
||||
j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 : |
||||
j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 : |
||||
(H1 ^ H2 ^ H3) - 899497514); |
||||
|
||||
H4 = H3; |
||||
H3 = H2; |
||||
H2 = (H1 << 30) | (H1 >>> 2); |
||||
H1 = H0; |
||||
H0 = t; |
||||
|
||||
} |
||||
|
||||
H0 += a; |
||||
H1 += b; |
||||
H2 += c; |
||||
H3 += d; |
||||
H4 += e; |
||||
|
||||
} |
||||
|
||||
return [H0, H1, H2, H3, H4]; |
||||
|
||||
}; |
||||
|
||||
// Package private blocksize
|
||||
SHA1._blocksize = 16; |
||||
|
||||
})(); |
@ -0,0 +1,39 @@ |
||||
/** |
||||
* @file 百度移动统计配置文件 |
||||
*/ |
||||
|
||||
module.exports = { |
||||
/** |
||||
* 从百度移动统计获取的AppKey |
||||
* @type {string} |
||||
*/ |
||||
appKey: 'ce2fa79380', |
||||
|
||||
/** |
||||
* 是否使用了插件 |
||||
* @type {boolean} |
||||
*/ |
||||
hasPlugin: false, |
||||
|
||||
/** |
||||
* 是否获取当前的地理位置和速度信息 |
||||
* @type {boolean} |
||||
*/ |
||||
getLocation: false, |
||||
|
||||
/** |
||||
* 是否获取组件滚动信息 |
||||
* @type {boolean} |
||||
*/ |
||||
getComponentScroll: false, |
||||
/** |
||||
* 是否开启了A/B 测试 |
||||
* @type {boolean} |
||||
*/ |
||||
hasABTest: false, |
||||
/** |
||||
* 是否开启热力图功能 |
||||
* @type {boolean} |
||||
*/ |
||||
hasHeatmap: false, |
||||
}; |
@ -0,0 +1,36 @@ |
||||
export default{ |
||||
// 监听用户点击右上角菜单的「转发」按钮时触发的事件
|
||||
onShareAppMessage() { |
||||
// 设置转发的参数
|
||||
return { |
||||
title: "职站商城", |
||||
// path: '',
|
||||
imageUrl: "", |
||||
success: function(res) { |
||||
if (res.errMsg == 'shareAppMessage:ok') { |
||||
console.log("成功", res) |
||||
} |
||||
}, |
||||
fail: function(res) { |
||||
console.log("失败", res) |
||||
} |
||||
} |
||||
}, |
||||
// 分享到朋友圈
|
||||
onShareTimeline:function(res){ |
||||
return { |
||||
title: '职站商城', |
||||
// imageUrl:'/static/image/phone.png',
|
||||
query:''
|
||||
} |
||||
}, |
||||
// 收藏
|
||||
onAddToFavorites:function(res) { |
||||
return { |
||||
title: '职站商城', |
||||
// imageUrl:'/static/image/phone.png',
|
||||
query: '', |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,18 @@ |
||||
|
||||
import uma from 'umtrack-wx'; |
||||
uma.init({ |
||||
appKey: '64cc98d5a1a164591b62da3e', // 由友盟分配的APP_KEY
|
||||
useOpenid: true, |
||||
// 使用Openid进行统计,此项为false时将使用友盟+uuid进行用户统计。
|
||||
// 使用Openid来统计微信小程序的用户,会使统计的指标更为准确,对系统准确性要求高的应用推荐使用Openid
|
||||
autoGetOpenid: true, |
||||
// 使用openid进行统计时,是否授权友盟自动获取Openid,
|
||||
// 如若需要,请到友盟后台"设置管理-应用信息"(https://mp.umeng.com/setting/appset)中设置appId及secret
|
||||
debug: true,// 是否打开调试模式
|
||||
uploadUserInfo: true, // 自动上传用户信息,设为false取消上传,默认为false
|
||||
enableVerify: true |
||||
}); |
||||
uma.install = function(Vue) { |
||||
Vue.prototype.$uma = uma; |
||||
} |
||||
export default uma; |
@ -0,0 +1,107 @@ |
||||
<template> |
||||
<view class="wrap"> |
||||
<view class="title">用户隐私协议</view> |
||||
<view class="text"> |
||||
我们非常重视对用户隐私的保护。您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本用户隐私条款向您说明,在使用我们的服务时,我们如何收集、使用、披露、存储这些信息。本用户隐私条款系本平台保护用户个人隐私的承诺,与您所使用的服务息息相关,希望您仔细阅读。 |
||||
(一)个人资料的收集 |
||||
您在注册账户或使用我们的服务时,向我们提供的相关个人信息,例如电话号码、电子邮件等;您通过我们的服务向其他方提供的共享信息,以及您使用我们的服务时所储存的信息。我们收集数据是根据您与我们的互动和您所做出的选择,包括您的隐私设置以及您使用的产品和功能。我们收集的数据可能包括SDK/API/JS代码版本、浏览器、互联网服务提供商、IP地址、平台、时间戳、应用标识符、应用程序版本、应用分发渠道、独立设备标识符、iOS广告标识符(IDFA)、安卓广告主标识符、网卡(MAC)地址、国际移动设备识别码(IMEI)、设备型号、终端制造厂商、终端设备操作系统版本、会话启动/停止时间、语言所在地、时区和网络状态(WiFi等)、硬盘、CPU和电池使用情况等。 |
||||
(二)个人资料的获取 |
||||
您使用服务时我们可能收集如下信息: |
||||
1、日志信息,指您使用我们的服务时,系统可能通过cookies、web、beacon或其他方式自动采集的技术信息,包括: |
||||
1)设备或软件信息,例如您的移动设备、网页浏览器或用于接入我们服务的其他程序所提供的配置信息、您的IP地址和移动设备所用的版本和设备识别码等;在使用我们服务时搜索或浏览的信息,例如您使用的网页搜索词语、访问的社交媒体页面url地址,以及您在使用我们服务时浏览或要求提供的其他信息和内容详情; |
||||
2)有关您曾使用的移动应用(APP)和其他软件的信息,以及您曾经使用该等移动应用和软件的信息; |
||||
3)您通过我们的服务进行通讯的信息,例如曾通讯的账号,以及通讯时间、数据和时长; |
||||
4)您通过我们的服务分享的内容所包含的信息(元数据),例如拍摄或上传的共享照片或录像的日期、时间或地点等。 |
||||
|
||||
2、位置信息,指您开启设备定位功能并使用我们基于位置提供的相关服务时,收集的有关您位置的信息,包括: |
||||
1)您通过具有定位功能的移动设备使用我们的服务时,通过GPS或WiFi等方式收集的您的地理位置信息; |
||||
2)您或其他用户提供的包含您所处地理位置的实时信息,例如您提供的账户信息中包含的您所在地区信息; |
||||
3)您可以通过关闭定位功能,停止对您的地理位置信息的收集。 |
||||
4)我们的产品集成友盟+SDK,友盟+SDK需要收集您的设备Mac地址、唯一设备识别码(IMEI/androidID/IDFA/OPENUDID/GUID、SIM卡IMSI信息)以提供统计分析服务。 |
||||
(三)APP涉及用户信息使用的SDK相关情况逐项列举,详情如下: |
||||
1、华为推送/小米推送/vivo推送/oppo推送: |
||||
SDK类型:推送通知; |
||||
SDK描述:用于实现消息推送(或其他推送)功能SDK; |
||||
使用业务场景:向用户推荐活动和提醒; |
||||
收集个人信息的类型:设备信息、地理位置、网络信息; |
||||
设备信息:设备标识符(IMEI、IDFA、Android、ID、MAC、OAID等相关信息)、应用信息(应用崩溃信息、通知开关状态、软件列表等相关信息)、设备参数及系统信息(设备类型、设备型号、操作系统及硬件相关信息); |
||||
网络信息:IP地址,WiFi信息,基站信息等相关信息; |
||||
使用目的/理由:向用户推荐活动和提醒。 |
||||
|
||||
2、微信开放平台: |
||||
SDK类型:社交; |
||||
SDK描述:微信分享功能; |
||||
SDK使用业务场景:微信分享; |
||||
SDK所需用户信息字段:软件安装列表、设备型号、MAC地址、IMEI号、IMSI、系统版本、手机型号; |
||||
使用目的/理由:APP分享音频、视频,图片,活动至微信客户端。 |
||||
|
||||
3、友盟分享: |
||||
SDK类型:社交; |
||||
SDK描述:分享功能; |
||||
SDK使用业务场景:分享到微信; |
||||
SDK所需用户信息字段:设备型号,系统版本号,手机型号,分享信息; |
||||
使用目的/理由:APP分享图片,活动至微信客户端。 |
||||
|
||||
4、友盟统计: |
||||
SDK类型:数据统计; |
||||
SDK描述:提供数据统计,数据收集,数据分析服务; |
||||
SDK使用业务场景:日活,路径分析; |
||||
SDK所需用户信息字段:设备型号,系统版本号,手机型号,发布渠道,页面code数据; |
||||
使用目的/理由:数据收集,进行数据分析,提升产品体验。 |
||||
|
||||
5、Bugly: |
||||
SDK类型:性能监测; |
||||
SDK描述:提供移动端应用运行时崩溃,卡顿监控服务; |
||||
SDK使用业务场景:对APP进行性能监控,提升产品使用体验; |
||||
SDK所需用户信息字段:设备型号,MAC地址,IMEI号,系统版本,手机型号; |
||||
使用目的/理由:监控app使用过程中的奔溃信息分析,提升用户体验。 |
||||
(四)个人资料的披露 |
||||
我们将采取合理的安全手段保护用户提供的个人及单位信息,在未得到用户许可之前,本平台不会擅自将用户信息披露给任何无关的第三方,但涉及下列情形之一的除外: |
||||
1、法律强制规定或司法行政机关依照法定程序要求提供。 |
||||
2、为保护用户的生命、财产安全或为公共安全之需要。 |
||||
3、为了保护本平台其他用户的合法权益或财产。 |
||||
4、您出现违反中国有关法律、法规或者本平台相关协议规则的情况,需要向第三方披露。 |
||||
5、其他特殊或紧急情况。 |
||||
由于用户对自身信息保密不当,从而导致用户资料的泄露,或由于网络线路、黑客攻击、计算机病毒等原因造成的资料泄露、丢失、被盗用或被篡改等,本平台不承担任何责任。 |
||||
(五)Cookies技术的使用 |
||||
我们收集信息是为了向您提供更好、更优、更个性化的服务,本公司将以合法的方式收集必要的用户个人资料。本平台有可能收集的个人资料包括:用户姓名、身份证号、地址、电话号码、电子邮件等信息。用户在本平台注册时,须依注册内容之提示提供用户本人及单位的真实、准确、完整信息,并保证个人及单位资料的及时更新。因用户提供个人及单位信息不准确、不完整或未及时更新而可能遭受的任何损害,本公司不承担任何责任。 |
||||
(六)个人资料的存储 |
||||
我们收集的有关您的信息和资料将保存在我们及(或)其关联公司的服务器上。 |
||||
(七)个人资料的保护 |
||||
为保障您的信息安全,我们将采取各种合理的安全措施来保护您的信息,使您的信息不会被泄漏、毁损或者丢失,我们对可能接触到您的信息的员工也采取了严格管理,包括但不限于根据岗位的不同采取不同的权限控制,与他们签署保密协议,监控他们的操作情况等措施。我们会按现有技术提供相应的安全措施来保护您的信息,提供合理的安全保障,尽力做到使您的信息不被泄漏、毁损或丢失。您的账户均有安全保护功能,请妥善保管您的账户及密码信息。我们将通过向其它服务器备份、对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。 |
||||
(八)未成年人保护 |
||||
我们重视未成年人的个人信息保护,如您为未成年人,建议您请您的监护人阅读本隐私权条款,并在征得您的监护人同意的前提下使用我们的服务或向我们提供信息。 |
||||
(九)联系我们 |
||||
您可通过发送邮件至service@huorantech.cn与我们沟通,我们将在15天内回复您的请求。 |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
|
||||
} |
||||
}, |
||||
methods: { |
||||
|
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.wrap { |
||||
padding: 30rpx; |
||||
.title { |
||||
margin-bottom: 30rpx; |
||||
font-size: 40rpx; |
||||
text-align: center; |
||||
} |
||||
.text { |
||||
font-size: 30rpx; |
||||
line-height: 1.6; |
||||
white-space: pre-wrap; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,487 @@ |
||||
<template> |
||||
<view class="wrap"> |
||||
<view class="banner-wrap bg-wh"> |
||||
<view class="pic-wrap"> |
||||
<image class="pic" :src="form.mall.coverDrawing" mode="widthFix"></image> |
||||
<view class="pro-title">{{ form.mall.productName }}</view> |
||||
</view> |
||||
<view class="fields"> |
||||
<view class="field"> |
||||
<image class="icon" src="@/static/image/product/1.png"></image> |
||||
市场建议价:{{ form.mall.marketUnitPrice || '' }}元/年 |
||||
</view> |
||||
<view class="field"> |
||||
<image class="icon" src="@/static/image/product/2.png"></image> |
||||
产品类型:{{ form.goodsRes.typeName || '' }} |
||||
</view> |
||||
</view> |
||||
</view> |
||||
|
||||
<view class="detail"> |
||||
<ul class="tabs"> |
||||
<li :class="{active: curTab === 0}" @click="tabChange(0)">详情介绍</li> |
||||
<li :class="{active: curTab === 1, disabled: !form.interfaceDiagram}" @click="tabChange(1)">界面图</li> |
||||
<li :class="{active: curTab === 2, disabled: !form.mallAnnex || !form.mallAnnex.length}" @click="tabChange(2)">参数</li> |
||||
</ul> |
||||
<view class="title"> |
||||
<image class="icon" src="@/static/image/product/3.png"></image> |
||||
产品详情 |
||||
</view> |
||||
<view class="texts"> |
||||
<view class="line"> |
||||
<text class="name">产品简介:</text> |
||||
<text class="val">{{ form.mall.productIntroduction }}</text> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">适用专业:</text> |
||||
<text class="val">{{ form.goodsRes.professionalName }}</text> |
||||
</view> |
||||
<view v-if="form.mall.courseHours" class="line"> |
||||
<text class="name">预计课时:</text> |
||||
<text class="val">{{ form.mall.courseHours }}</text> |
||||
</view> |
||||
<view v-if="form.mall.matchingCourse" class="line"> |
||||
<text class="name">匹配课程:</text> |
||||
<text class="val">{{ form.mall.matchingCourse }}</text> |
||||
</view> |
||||
<view v-if="form.mall.applicationScenario" class="line"> |
||||
<text class="name">适用场景:</text> |
||||
<text class="val">{{ form.mall.applicationScenario }}</text> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">详情介绍:</text> |
||||
<mp-html class="des-html" :tag-style="mpStyle" :content="form.mall.detailedIntroduction"/> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
|
||||
<view v-if="form.interfaceDiagram" class="detail" id="pics"> |
||||
<view class="title mb"> |
||||
<image class="icon" src="@/static/image/product/4.png"></image> |
||||
产品界面图 |
||||
</view> |
||||
<uni-swiper-dot :info="form.interfaceDiagram.length > 1 ? form.interfaceDiagram : 0" field="content" mode="round" :dotsStyles="dotsStyles"> |
||||
<swiper class="swiper-box" autoplay> |
||||
<swiper-item v-for="(pic, i) in form.interfaceDiagram" :key="i"> |
||||
<image class="pic" mode="widthFix" :src="pic"></image> |
||||
</swiper-item> |
||||
</swiper> |
||||
</uni-swiper-dot> |
||||
</view> |
||||
|
||||
<view v-if="form.mallAnnex && form.mallAnnex.length" class="detail" id="annex"> |
||||
<view class="title-wrap"> |
||||
<view class="title"> |
||||
<image class="icon" src="@/static/image/product/5.png"></image> |
||||
产品参数 |
||||
</view> |
||||
<view v-if="auth('产品:下载')" class="download" @click="downloadAll"> |
||||
下载全部 |
||||
<image class="icon" src="@/static/image/product/6.png"></image> |
||||
</view> |
||||
</view> |
||||
|
||||
<view v-if="form.mallAnnex" class="files"> |
||||
<view v-for="(file, i) in form.mallAnnex" :key="i" class="file"> |
||||
<view class="text"> |
||||
<image class="icon" src="@/static/image/product/ppt.png"></image> |
||||
{{ file.fileName }} |
||||
</view> |
||||
<view v-if="auth('产品:下载')" class="download" @click="download(file)">下载</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
|
||||
<view class="footer"> |
||||
<view v-if="auth('产品:购物车')" class="shop" @click="$util.to('../shopCart/shopCart')"> |
||||
<uni-badge size="small" :text="shopCartTotal" absolute="topRight" type="error"> |
||||
<image class="icon" src="@/static/image/product/shop.png" mode="widthFix"></image> |
||||
</uni-badge> |
||||
<view>购物车</view> |
||||
</view> |
||||
|
||||
<view class="btns"> |
||||
<view v-if="auth('产品:加入购物车')" class="btn" @click="addShop">加入购物车</view> |
||||
<view v-if="auth('产品:下单')" class="btn order" @click="order">下单</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { detailsOfGoods, addToShoppingCart, shoppingCartList } from '@/apis/modules/product.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
id: '', |
||||
curTab: 0, |
||||
tabs: [ |
||||
{ |
||||
name: '详情介绍', |
||||
id: 0 |
||||
}, |
||||
{ |
||||
name: '界面图', |
||||
id: 1 |
||||
}, |
||||
{ |
||||
name: '参数', |
||||
id: 2 |
||||
} |
||||
], |
||||
form: { |
||||
classificationIds: [], |
||||
interfaceDiagram: [], |
||||
mall: { |
||||
coverDrawing: '' |
||||
}, |
||||
goodsRes: {}, |
||||
mallAnnex: [] |
||||
}, |
||||
shopCartTotal: 0, |
||||
dotsStyles: { |
||||
backgroundColor: 'rgba(83, 200, 249,0.3)', |
||||
border: '1px rgba(83, 200, 249,0.3) solid', |
||||
color: '#fff', |
||||
selectedBackgroundColor: 'rgba(83, 200, 249,0.9)', |
||||
selectedBorder: '1px rgba(83, 200, 249,0.9) solid' |
||||
}, |
||||
mpStyle: { |
||||
p: 'font-size: 25rpx !important;font-family: Microsoft Yahei !important;font-weight: 400 !important;color: #333 !important;', |
||||
span: 'font-size: 25rpx !important;font-family: Microsoft Yahei !important;font-weight: 400 !important;color: #333 !important;' |
||||
} |
||||
} |
||||
}, |
||||
onShow() { |
||||
const pages = getCurrentPages() |
||||
const { options } = pages[pages.length - 1] |
||||
this.id = options.id |
||||
this.getInfo() |
||||
this.getShopCart() |
||||
// 清除订单缓存 |
||||
try { |
||||
uni.removeStorageSync('orderForm') |
||||
uni.removeStorageSync('courses') |
||||
} catch (e) {} |
||||
}, |
||||
methods: { |
||||
// 获取详情 |
||||
getInfo() { |
||||
uni.showLoading({ |
||||
title: '加载中' |
||||
}) |
||||
detailsOfGoods(this.id).then(res => { |
||||
const e = res.orderDetails |
||||
e.mall.productIntroduction = this.$util.removeTag(e.mall.productIntroduction) |
||||
// e.mall.detailedIntroduction = this.$util.removeTag(e.mall.detailedIntroduction) |
||||
this.form = e |
||||
const pics = this.form.mall.interfaceDiagram |
||||
if (pics) { |
||||
this.form.interfaceDiagram = pics.split(',') |
||||
// this.tabs.push({ |
||||
// name: '界面图', |
||||
// id: 1 |
||||
// }) |
||||
} |
||||
// this.form.mallAnnex && this.form.mallAnnex.length && this.tabs.push({ |
||||
// name: '参数', |
||||
// id: 2 |
||||
// }) |
||||
uni.hideLoading() |
||||
}).catch(e => { |
||||
uni.hideLoading() |
||||
}) |
||||
}, |
||||
// 获取购物车数量 |
||||
getShopCart() { |
||||
shoppingCartList({ |
||||
pageNum: 1, |
||||
pageSize: 1000, |
||||
}).then(({ data }) => { |
||||
this.shopCartTotal = data.total |
||||
}).catch(e => {}) |
||||
}, |
||||
// tab切换 |
||||
tabChange(id) { |
||||
if ((id === 1 && !this.form.interfaceDiagram) || (id === 2 && (!form.mallAnnex || !form.mallAnnex.length))) return false |
||||
this.curTab = id |
||||
id == 1 && uni.pageScrollTo({ |
||||
selector: '#pics', |
||||
}) |
||||
id == 2 && uni.pageScrollTo({ |
||||
selector: '#annex', |
||||
}) |
||||
}, |
||||
// 下载全部 |
||||
downloadAll() { |
||||
const fileName = [] |
||||
const urls = [] |
||||
this.form.mallAnnex.forEach(e => { |
||||
fileName.push(e.fileName) |
||||
urls.push(e.filePath) |
||||
}) |
||||
uni.setStorageSync('files', { |
||||
copyWriting: this.form.mall.productName, |
||||
fileName, |
||||
urls |
||||
}) |
||||
this.$util.to(`/team/send/send`) |
||||
}, |
||||
// 下载附件 |
||||
download(item) { |
||||
uni.setStorageSync('files', { |
||||
copyWriting: this.form.mall.productName, |
||||
fileName: [item.fileName], |
||||
urls: [item.filePath] |
||||
}) |
||||
this.$util.to(`/team/send/send`) |
||||
}, |
||||
// 加入购物车 |
||||
addShop() { |
||||
uni.showLoading({ |
||||
title: '加载中' |
||||
}) |
||||
addToShoppingCart({ |
||||
mallId: this.id |
||||
}).then(res => { |
||||
this.$util.sucMsg('加购成功') |
||||
uni.hideLoading() |
||||
this.getShopCart() |
||||
}).catch(e => { |
||||
uni.hideLoading() |
||||
}) |
||||
}, |
||||
// 下单 |
||||
order() { |
||||
const { mall, typeIds } = this.form |
||||
const classificationId = this.form.classificationIds[0] |
||||
const authority = this.$util.getOrderType(classificationId) |
||||
// 把该产品添加至缓存 |
||||
uni.setStorageSync('courses', [{ |
||||
dataOrCourseId: mall.associatedProduct, // id |
||||
mallId: mall.mallId, |
||||
productName: mall.productName, // 名称 |
||||
periodOfUse: '', // 使用期限 |
||||
startTime: this.$util.formatDate(new Date(), 'yyyy-MM-dd'), // 开始 |
||||
endTime: '', // 终止 |
||||
remainingPeriod: '', // 剩余期限 |
||||
marketValue: '', // 市场价 |
||||
marketPrice: mall.marketUnitPrice, // 市场单价 |
||||
finalPrice: 0, // 成交价 |
||||
finalValue: 0, // 成交单价(数据产品特有) |
||||
discountRate: '0%', // 折扣率 |
||||
accountNum: 1, // 账号数 |
||||
totalAmount: '', // 总价 |
||||
isEnable: 0, // 启用否:1启用,0禁用 |
||||
ship: 0, // 发货否(0未发货,1已发货,默认不发货) |
||||
authority, // 区分权限 |
||||
options: 2, |
||||
miniProgramPictureAddress: mall.appletIcon || '', // 图标 |
||||
settlementPrice: 0, // 结算价 |
||||
settlementPriceUnit: 0, // 结算单价 |
||||
serviceFee: 0, // 平台服务费(前端计算后展示,不入库) |
||||
typeId: typeIds && typeIds.length ? typeIds[0] : '', |
||||
classificationId, |
||||
}]) |
||||
this.$util.to(`../orderDetail/orderDetail?shopCart=1`) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.wrap { |
||||
padding-bottom: 140rpx; |
||||
} |
||||
.banner-wrap { |
||||
width: 100%; |
||||
border-radius: 0px 0px 20px 20px; |
||||
.pic-wrap { |
||||
padding: 20rpx 32rpx; |
||||
border-bottom: 1px solid #E2E2E2; |
||||
} |
||||
.pic { |
||||
width: 100%; |
||||
} |
||||
.pro-title { |
||||
margin-top: 22rpx; |
||||
font-size: 30rpx; |
||||
font-weight: 600; |
||||
color: #333; |
||||
} |
||||
.fields { |
||||
padding: 26rpx 32rpx; |
||||
} |
||||
.field { |
||||
display: flex; |
||||
align-items: center; |
||||
font-size: 28rpx; |
||||
color: #333; |
||||
&:first-child { |
||||
margin-bottom: 22rpx; |
||||
} |
||||
.icon { |
||||
width: 36rpx; |
||||
height: 36rpx; |
||||
margin-right: 12rpx ; |
||||
} |
||||
} |
||||
} |
||||
.tabs { |
||||
display: flex; |
||||
margin-bottom: 32rpx; |
||||
li { |
||||
width: 33.33%; |
||||
text-align: center; |
||||
font-size: 28rpx; |
||||
color: #333; |
||||
white-space: nowrap; |
||||
} |
||||
.active { |
||||
color: #007EFF; |
||||
&:after { |
||||
content: ''; |
||||
display: block; |
||||
width: 40rpx; |
||||
height: 8rpx; |
||||
margin: 18rpx auto 0; |
||||
border-radius: 4px; |
||||
background-color: #007EFF; |
||||
} |
||||
} |
||||
.disabled { |
||||
color: #b3b3b3; |
||||
} |
||||
} |
||||
.detail { |
||||
padding: 34rpx 32rpx; |
||||
margin: 16rpx 0; |
||||
border-radius: 20px; |
||||
background-color: #fff; |
||||
.title-wrap { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
.download { |
||||
display: inline-flex; |
||||
align-items: center; |
||||
padding: 7rpx 16rpx; |
||||
font-size: 24rpx; |
||||
color: #007EFF; |
||||
text-align: center; |
||||
background-color: #F2F8FF; |
||||
.icon { |
||||
width: 30rpx; |
||||
height: 30rpx; |
||||
margin-left: 4rpx; |
||||
} |
||||
} |
||||
} |
||||
.title { |
||||
display: flex; |
||||
align-items: center; |
||||
font-size: 30rpx; |
||||
font-weight: 600; |
||||
color: #333 ; |
||||
.icon { |
||||
width: 36rpx; |
||||
height: 36rpx; |
||||
margin-right: 11rpx; |
||||
} |
||||
&.mb { |
||||
margin-bottom: 32rpx; |
||||
} |
||||
} |
||||
.texts { |
||||
margin-top: 32rpx; |
||||
} |
||||
.line { |
||||
display: flex; |
||||
margin-bottom: 18rpx; |
||||
line-height: 40rpx; |
||||
text { |
||||
font-size: 26rpx; |
||||
color: #333; |
||||
} |
||||
.name { |
||||
font-weight: 600; |
||||
white-space: nowrap; |
||||
} |
||||
.des-html { |
||||
width: calc(100% - 140rpx); |
||||
font-size: 28rpx !important; |
||||
} |
||||
.rich-text-content span { |
||||
font-size: 28rpx; |
||||
color: #f00; |
||||
} |
||||
} |
||||
.swiper-box { |
||||
min-height: 250px; |
||||
} |
||||
.pic { |
||||
width: 100%; |
||||
} |
||||
.files { |
||||
margin-top: 34rpx; |
||||
.file { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
margin-bottom: 24rpx; |
||||
.text { |
||||
display: inline-flex; |
||||
align-items: center; |
||||
font-size: 26rpx; |
||||
color: #333; |
||||
} |
||||
.icon { |
||||
width: 36rpx; |
||||
height: 36rpx; |
||||
margin-right: 14rpx; |
||||
} |
||||
.download { |
||||
font-size: 26rpx; |
||||
color: #007EFF; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
.footer { |
||||
position: fixed; |
||||
bottom: 0; |
||||
left: 0; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
width: 100%; |
||||
padding: 24rpx 32rpx 24rpx 73rpx; |
||||
background-color: #fff; |
||||
box-sizing: border-box; |
||||
.shop { |
||||
text-align: center; |
||||
font-size: 24rpx; |
||||
color: #333; |
||||
.icon { |
||||
width: 56rpx; |
||||
} |
||||
} |
||||
.btns { |
||||
display: inline-flex; |
||||
.btn { |
||||
width: 229rpx; |
||||
line-height: 80rpx; |
||||
text-align: center; |
||||
font-size: 32rpx; |
||||
color: #333; |
||||
background-color: #E5EEF7; |
||||
border-radius: 100px 0px 0px 100px; |
||||
} |
||||
.order { |
||||
color: #fff; |
||||
background-color: #007EFF; |
||||
border-radius: 0px 100px 100px 0px; |
||||
} |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,399 @@ |
||||
<template> |
||||
<view> |
||||
<view class="filter"> |
||||
<uni-search-bar class="search" radius="30" placeholder="请输入产品名称" v-model="keyword" clearButton="auto" cancelButton="none" /> |
||||
<uni-icons class="icon" custom-prefix="iconfont" type="icon-filter" size="22" color="#007eff" @click="popup = true"></uni-icons> |
||||
</view> |
||||
|
||||
<ul class="tab-wrap"> |
||||
<view class="tab"> |
||||
<li :class="{active: curTab === ''}" @click="tabChange('')">全部</li> |
||||
</view> |
||||
|
||||
<scroll-view scroll-x :scroll-left="scrollLeft" class="tab tab-scroll"> |
||||
<li v-for="(tab, i) in tabs" :key="i" :class="{active: curTab === tab.value}" @click="tabChange(tab.value)">{{ tab.title }}</li> |
||||
</scroll-view> |
||||
</ul> |
||||
|
||||
<view class="tags"> |
||||
<view v-if="categoryName" class="tag" @click="delCategory"> |
||||
{{ categoryName }} |
||||
<uni-icons class="icon" type="closeempty" size="15" color="#007EFF"></uni-icons> |
||||
</view> |
||||
<view v-if="productTypeName" class="tag" @click="delProductType"> |
||||
{{ productTypeName }} |
||||
<uni-icons class="icon" type="closeempty" size="15" color="#007EFF"></uni-icons> |
||||
</view> |
||||
<view v-if="form.selection" class="tag" @click="delSelection"> |
||||
官方精选 |
||||
<uni-icons class="icon" type="closeempty" size="15" color="#007EFF"></uni-icons> |
||||
</view> |
||||
<view v-if="tagName" class="tag" @click="delTag"> |
||||
{{ tagName }} |
||||
<uni-icons class="icon" type="closeempty" size="15" color="#007EFF"></uni-icons> |
||||
</view> |
||||
</view> |
||||
|
||||
<ul class="list"> |
||||
<li v-for="(item, i) in list" :key="i" @click="toDetail(item)"> |
||||
<view class="pro-name"> |
||||
<image class="icon" :src="$util.getIcon(item)"></image> |
||||
{{ item.productName }} |
||||
</view> |
||||
<view class="info"> |
||||
<view class="line"> |
||||
<text class="name">产品简介:</text> |
||||
<view class="val ell-wrap"> |
||||
<view class="ell">{{ item.productIntroduction }}</view> |
||||
</view> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">产品类型:</text> |
||||
<text class="val">{{ item.typeName }}</text> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">适用专业:</text> |
||||
<text class="val">{{ item.professionalName }}</text> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">市场建议单价:</text> |
||||
<text class="val">{{ item.marketUnitPrice }}元/年</text> |
||||
</view> |
||||
</view> |
||||
</li> |
||||
</ul> |
||||
|
||||
<view v-if="auth('产品:购物车')" class="plus"> |
||||
<uni-badge size="small" :text="total" absolute="topRight" type="error"> |
||||
<image class="icon" src="@/static/image/product/shop-blue.png" mode="widthFix" @click="$util.to('../shopCart/shopCart')"></image> |
||||
</uni-badge> |
||||
</view> |
||||
|
||||
<filter-popup ref="filter" showCategory :data="filters" :form.sync="filterForm" v-model="popup" title="全部筛选" height="1104rpx" @finsh="subFinsh"></filter-popup> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { tagsList, listOfGoods, productTypeList, shoppingCartList } from '@/apis/modules/product.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
popup: false, |
||||
//筛选表单数据 |
||||
filters: [ |
||||
{ |
||||
children: false,//是否有子项 |
||||
title: "产品类型", |
||||
key: "productType", //键名 接收对象名字 |
||||
keyValue: "value", //获取的值是哪个 |
||||
isRadio: true, //是否单选 否则多选 |
||||
data: [], |
||||
}, |
||||
{ |
||||
children: false,//是否有子项 |
||||
title: "官方精选", |
||||
key: "selection", //键名 接收对象名字 |
||||
keyValue: "value", //获取的值是哪个 |
||||
data: [ |
||||
{ |
||||
value: 1, |
||||
title: '官方精选' |
||||
} |
||||
], |
||||
}, |
||||
{ |
||||
children: false,//是否有子项 |
||||
title: "产品标签", |
||||
key: "tagId", //键名 接收对象名字 |
||||
keyValue: "value", //获取的值是哪个 |
||||
isRadio: true, //是否单选 否则多选 |
||||
data: [], |
||||
}, |
||||
], |
||||
filterForm: { |
||||
productType: [], |
||||
selection: [], |
||||
tagId: [] |
||||
}, |
||||
form: { |
||||
categoryId: '', |
||||
professionalCategoryId: '', |
||||
professionalId: '', |
||||
productType: '', |
||||
selection: '', |
||||
tagId: '' |
||||
}, |
||||
tagId: '', |
||||
curTab: '', |
||||
tabs: [], |
||||
scrollLeft: 0, |
||||
reachBottom: 0, // 是否是上拉加载。0->否,1->是,-1->加载完所有数据 |
||||
status: 'more', // 上拉加载状态 more|loading|noMore |
||||
searchTimer: null, |
||||
sort: 0, |
||||
keyword: '', |
||||
list: [], |
||||
page: 1, |
||||
pageSize: 10, |
||||
total: 0, |
||||
cartNum: '', |
||||
productTypeName: '', |
||||
tagName: '', |
||||
categoryName: '', |
||||
} |
||||
}, |
||||
watch: { |
||||
keyword () { |
||||
clearTimeout(this.searchTimer) |
||||
this.searchTimer = setTimeout(() => { |
||||
this.initList() |
||||
}, 500) |
||||
} |
||||
}, |
||||
// 下拉刷新 |
||||
onPullDownRefresh() { |
||||
this.initList() |
||||
setTimeout(() => { |
||||
uni.stopPullDownRefresh() |
||||
}, 1500) |
||||
}, |
||||
// 上拉加载 |
||||
onReachBottom() { |
||||
if (this.reachBottom >= 0) { |
||||
this.reachBottom = 1 |
||||
this.status = 'loading' |
||||
this.getList() |
||||
} |
||||
}, |
||||
onShow() { |
||||
const pages = getCurrentPages() |
||||
const { options } = pages[pages.length - 1] |
||||
const { tagId, tagsName } = options |
||||
this.form.tagId = +tagId || '' |
||||
if (tagId) { |
||||
this.filterForm.tagId = [+tagId] |
||||
// this.tagName = tagsName |
||||
} |
||||
|
||||
this.keyword = options.keyword || '' |
||||
this.page = 1 |
||||
this.getList() |
||||
this.getFilter() |
||||
this.getShopCart() |
||||
}, |
||||
methods: { |
||||
getList() { |
||||
listOfGoods({ |
||||
pageNum: this.page, |
||||
pageSize: this.pageSize, |
||||
sort: 0, |
||||
isShelves: 0, |
||||
hotTag: this.form.tagId ? 2 : 1, |
||||
...this.form, |
||||
productName: this.keyword, |
||||
productType: this.curTab |
||||
}).then(({ page }) => { |
||||
// 未加载完所有数据,并且不是筛选,则拼接list,否则直接赋值 |
||||
const list = page.records |
||||
list.map(e => { |
||||
e.productIntroduction = this.$util.removeTag(e.productIntroduction) |
||||
}) |
||||
this.list = this.reachBottom > 0 ? [...this.list, ...list] : list |
||||
this.page++ // 每次获取了数据后page+1 |
||||
const noMore = this.list.length === page.total // 是否加载完所有数据 |
||||
this.status = noMore ? 'noMore' : 'more' // 加载完了则设置为noMore |
||||
this.reachBottom = noMore ? -1 : 0 // 加载完了则设置为-1 |
||||
}).catch(e => {}) |
||||
}, |
||||
initList() { |
||||
this.page = 1 |
||||
this.reachBottom = 0 |
||||
this.getList() |
||||
}, |
||||
// 获取购物车数量 |
||||
getShopCart() { |
||||
shoppingCartList({ |
||||
pageNum: 1, |
||||
pageSize: 1000, |
||||
}).then(({ data }) => { |
||||
this.total = data.total |
||||
}).catch(e => {}) |
||||
}, |
||||
// 筛选 |
||||
getFilter() { |
||||
// 产品类型 |
||||
productTypeList().then(res => { |
||||
res.typeList.forEach(e => { |
||||
e.value = e.typeId |
||||
e.title = e.typeName |
||||
}) |
||||
this.tabs = res.typeList |
||||
this.filters[0].data = res.typeList |
||||
}).catch(e => {}) |
||||
|
||||
// 产品标签 |
||||
tagsList().then(res => { |
||||
res.tagsList.forEach(e => { |
||||
e.value = e.tagsId |
||||
e.title = e.tagsName |
||||
}) |
||||
this.filters[2].data = res.tagsList |
||||
}).catch(e => {}) |
||||
}, |
||||
// 筛选确定回调 |
||||
subFinsh(val) { |
||||
const { productType, selection, tagId, categoryId, professionalCategoryId, professionalId, categoryName } = val |
||||
this.form = { |
||||
categoryId: categoryId || '', |
||||
professionalCategoryId: professionalCategoryId || '', |
||||
professionalId: professionalId || '', |
||||
productType: productType.length ? productType[0] : '', |
||||
selection: selection.length ? selection[0] : '', |
||||
tagId: tagId.length ? tagId[0] : '' |
||||
} |
||||
this.categoryName = categoryName || '' |
||||
this.productTypeName = this.form.productType ? this.filters[0].data.find(e => e.value == this.form.productType).title : '' |
||||
this.tagName = this.form.tagId ? this.filters[2].data.find(e => e.value == this.form.tagId).title : '' |
||||
this.initList() |
||||
}, |
||||
// 删除学科专业已选 |
||||
delCategory() { |
||||
this.$refs.filter.category = [] |
||||
this.$refs.filter.categoryName = '' |
||||
this.$refs.filter.categoryId = '' |
||||
this.$refs.filter.professionalCategoryId = '' |
||||
this.$refs.filter.professionalId = '' |
||||
this.form.categoryId = '' |
||||
this.form.professionalCategoryId = '' |
||||
this.form.professionalId = '' |
||||
this.categoryName = '' |
||||
this.initList() |
||||
}, |
||||
// 删除产品类型已选 |
||||
delProductType() { |
||||
this.filterForm.productType = [] |
||||
this.form.productType = '' |
||||
this.productTypeName = '' |
||||
this.initList() |
||||
}, |
||||
// 删除官方精选已选 |
||||
delSelection() { |
||||
this.filterForm.selection = [] |
||||
this.form.selection = '' |
||||
this.initList() |
||||
}, |
||||
// 删除产品标签已选 |
||||
delTag() { |
||||
this.filterForm.tagId = [] |
||||
this.form.tagId = '' |
||||
this.tagName = '' |
||||
this.initList() |
||||
}, |
||||
// tab切换 |
||||
tabChange(id) { |
||||
this.curTab = id |
||||
this.initList() |
||||
}, |
||||
// 跳转详情 |
||||
toDetail(item) { |
||||
console.log(44, item) |
||||
this.$util.to(`../productDetail/productDetail?id=${item.mallId}`) |
||||
}, |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.tab-wrap { |
||||
display: flex; |
||||
.tab-scroll { |
||||
width: calc(100% - 100rpx); |
||||
white-space: nowrap; |
||||
li { |
||||
display: inline-block; |
||||
} |
||||
} |
||||
} |
||||
.tags { |
||||
display: flex; |
||||
align-items: center; |
||||
flex-wrap: wrap; |
||||
padding: 0 24rpx; |
||||
.tag { |
||||
display: inline-flex; |
||||
align-items: center; |
||||
padding: 10rpx 14rpx; |
||||
margin: 0 20rpx 16rpx 0; |
||||
font-size: 28rpx; |
||||
color: #007EFF; |
||||
background-color: #fff; |
||||
border-radius: 4px; |
||||
} |
||||
.icon { |
||||
margin-left: 6rpx; |
||||
} |
||||
} |
||||
.list { |
||||
li { |
||||
padding: 0 24rpx; |
||||
margin: 16rpx 24rpx; |
||||
background-color: #fff; |
||||
border-radius: 16rpx; |
||||
} |
||||
.pro-name { |
||||
display: flex; |
||||
align-items: center; |
||||
padding: 18rpx 0; |
||||
font-size: 30rpx; |
||||
font-weight: 600; |
||||
color: #333; |
||||
border-bottom: 1px solid #E6E8ED; |
||||
.icon { |
||||
width: 58rpx; |
||||
min-width: 58rpx; |
||||
height: 58rpx; |
||||
margin-right: 20rpx; |
||||
border-radius: 4px; |
||||
} |
||||
} |
||||
.info { |
||||
padding: 12rpx 0; |
||||
} |
||||
.line { |
||||
display: flex; |
||||
padding: 12rpx 0; |
||||
} |
||||
.name { |
||||
margin-right: 10rpx; |
||||
font-size: 28rpx; |
||||
color: #999; |
||||
} |
||||
.val { |
||||
max-width: 70%; |
||||
font-size: 28rpx; |
||||
color: #333; |
||||
} |
||||
.ell-wrap { |
||||
display: inline-flex; |
||||
align-items: center; |
||||
} |
||||
.ell { |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
} |
||||
.toggle { |
||||
margin-left: 10rpx; |
||||
white-space: nowrap; |
||||
font-size: 24rpx; |
||||
color: #0e92ef; |
||||
} |
||||
} |
||||
.plus { |
||||
bottom: 140rpx; |
||||
right: 60rpx; |
||||
.icon { |
||||
width: 102rpx; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,88 @@ |
||||
<template> |
||||
<view class="wrap"> |
||||
<view class="title">服务协议</view> |
||||
<view class="text">我们提醒您:在使用本平台的各项服务之前,请您务必仔细阅读并透彻理解本声明。如果您使用本平台服务的,您的使用行为将被视为对本声明全部内容的认可,若您不同意本声明中的全部或部分内容,您应立即停止使用本平台服务。</view> |
||||
<view class="text">一、知识产权声明</view> |
||||
<view class="text">1、本平台内的所有产品、技术、软件、程序、数据及其他信息(包括但不限于文字、图像、图片、照片、音频、视频、图表、色彩、版面设计、电子文档)的所有权利(包括但不限于域名、版权、商标权、专利权、商业秘密及其他所有相关权利)均归我们公司或其关联公司所有。未经我司的许可,任何人不得擅自使用。</view> |
||||
<view class="text">2、本平台的Logo等文字、图形及其组合,以及其他标识、徵记、产品和服务名称均为我司及其关联公司的注册商标,未经我们的书面授权,任何人不得以任何方式使用或作其他处理,也不得向他人表明您有权使用或作其他处理。</view> |
||||
<view class="text">3、如果本平台内容无权利声明,并不代表本平台对其不享有权利,也不意味着本平台不主张权利,您应根据诚信原则尊重该等内容的合法权益并进行合法使用。您不得以任何方式修改、复制、公开展示、公布或分发这些材料或者以其他方式把它们用于任何公开或商业目的。禁止以任何目的把这些材料用于其他任何网站或其他平面媒体或网络计算机环境。</view> |
||||
<view class="text">二、服务声明</view> |
||||
<view class="text">1、我们向用户提供的所有产品或新增加的服务功能,均适用本声明条款之规范。</view> |
||||
<view class="text">2、本平台所有信息、软件均为按现状提供,不带有任何明示或暗示的担保或条件,包括但不限于准确性、及时性和非侵权的默示担保或保证。</view> |
||||
<view class="text">3、本平台有权根据业务需要修订本“服务声明”,并以平台公告的形式进行更新,不再单独通知予您。经修订的“服务声明”一经本平台公布,即产生效力。如您不同意相关修订,请您立即停止使用本平台服务。如您继续使用服务,则将视为您已接受经修订的条款,当您与本平台发生争议时,应以最新的条款为准。</view> |
||||
<view class="text">三、平台的使用</view> |
||||
<view class="text">1、除法定许可或征得本公司同意,本平台的信息及其任何组成部分不得被重新编辑、复制、抄袭,或为任何未经本公司允许的商业目的所使用。如果本公司确定用户行为违法或有损本平台和本公司的合法权益,本公司将采取相关法律措施,包括但不限于拒绝提供服务、冻结或删除用户账号等。</view> |
||||
<view class="text">2、如果您从本站下载软件,在使用软件时要遵守该软件附带的软件许可协议中所有的许可条款。在您阅读并接受软件许可协议的各项条款之前,不得下载或安装这一软件。如果在适用的许可条款或协议中,已经禁止复制或再分发这些软件,您须遵照执行。</view> |
||||
<view class="text">四、信息发布条款</view> |
||||
<view class="text">(一)平台信息发布</view> |
||||
<view class="text">本平台所发布的信息由所有权人及其关联公司遵循真实原则发布,发布人对平台信息不作任何保证或其它担保,包括适销性、适合于特定目的、没有计算机病毒或不侵犯知识产权的保证。</view> |
||||
<view class="text">(二)用户信息发布</view> |
||||
<view class="text">1、平台用户有权在本平台允许用户发布信息的版块发布信息,用户在本平台发表或投递的信息、回复必须遵守中华人民共和国各项法律、法规、条例,不发布或链接有关政治、破坏系统、淫秽色情、封建迷信、人身攻击等违法信息,不侵犯他人知识产权等。</view> |
||||
<view class="text">2、我们作为服务提供平台,用户在使用时应当了解明白平台上所有信息均为用户自由发布,用户应依法对其提供的任何信息承担全部责任。我们会对信息进行必要的核查(筛选),但最终对信息的合法性、准确性、真实性不承担任何法律责任。如用户发现某些信息中含有虚假、违法内容,请及时联系我们, 待核实之后,我们将根据中国法律法规和政府规范性文件采取措施移除相关内容或屏蔽相关链接。我们不对用户所发布的信息之删除或储存失败负责。若因用户发布内容引起任何刑事或民事纠纷,发布者须自行承担该刑事、民事或者经济法律责任,同时本平台有权就发布者上述违法行为给予平台造成的任何损失要求赔偿。</view> |
||||
<view class="text">五、隐私权政策</view> |
||||
<view class="text">我们非常重视对用户隐私的保护。您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本用户隐私条款向您说明,在使用我们的服务时,我们如何收集、使用、披露、存储这些信息。本用户隐私条款系本平台保护用户个人隐私的承诺,与您所使用的服务息息相关,希望您仔细阅读。</view> |
||||
<view class="text">(一)个人资料的收集</view> |
||||
<view class="text">我们收集信息是为了向您提供更好、更优、更个性化的服务,本公司将以合法的方式收集必要的用户个人资料。本平台有可能收集的个人资料包括:用户姓名、身份证号、地址、电话号码、电子邮件等信息。用户在本平台注册时,须依注册内容之提示提供用户本人及单位的真实、准确、完整信息,并保证个人及单位资料的及时更新。因用户提供个人及单位信息不准确、不完整或未及时更新而可能遭受的任何损害,本公司不承担任何责任。</view> |
||||
<view class="text">(二)个人资料的使用</view> |
||||
<view class="text">本公司有权为内部经营、管理、统计等目的使用您提供的个人及单位资料,包括但不限于:日常管理本公司提供给用户的服务及产品、监控本平台的安全使用、内部调研、对来访数据进行统计和研究;促进更新供用户享用的服务和产品;确认核对联络名单、为宣传推广目的;为解决争议、排除纠纷和执行本法律声明目的等。</view> |
||||
<view class="text">(三)个人资料的披露</view> |
||||
<view class="text">我们将采取合理的安全手段保护用户提供的个人及单位信息,在未得到用户许可之前,本平台不会擅自将用户信息披露给任何无关的第三方,但涉及下列情形之一的除外:</view> |
||||
<view class="text">1、法律强制规定或司法行政机关依照法定程序要求提供。</view> |
||||
<view class="text">2、为保护用户的生命、财产安全或为公共安全之需要。</view> |
||||
<view class="text">3、为了保护本平台其他用户的合法权益或财产。</view> |
||||
<view class="text">4、您出现违反中国有关法律、法规或者本平台相关协议规则的情况,需要向第三方披露。</view> |
||||
<view class="text">5、其他特殊或紧急情况。</view> |
||||
<view class="text">由于用户对自身信息保密不当,从而导致用户资料的泄露,或由于网络线路、黑客攻击、计算机病毒等原因造成的资料泄露、丢失、被盗用或被篡改等,本平台不承担任何责任。</view> |
||||
<view class="text">(四)Cookies技术的使用</view> |
||||
<view class="text">当用户访问设有Cookies装置的本平台时,本平台服务器会自动发送Cookies至用户浏览器中,同时储存进用户的电脑硬盘内,此Cookies便负责记录日后用户访问本平台时的种种操作、浏览习惯、信用记录等。运用Cookies技术,我们能够为您提供更加周到的个性化服务。我们将运用Cookies技术向用户提供其感兴趣的信息资料或为其储存密码。</view> |
||||
<view class="text">(五)个人资料的存储</view> |
||||
<view class="text">我们收集的有关您的信息和资料将保存在本公司及(或)其关联公司的服务器上。</view> |
||||
<view class="text">(六)个人资料的保护</view> |
||||
<view class="text">为保障您的信息安全,我们将采取各种合理的安全措施来保护您的信息,使您的信息不会被泄漏、毁损或者丢失,我们对可能接触到您的信息的员工也采取了严格管理,包括但不限于根据岗位的不同采取不同的权限控制,与他们签署保密协议,监控他们的操作情况等措施。我们会按现有技术提供相应的安全措施来保护您的信息,提供合理的安全保障,尽力做到使您的信息不被泄漏、毁损或丢失。您的账户均有安全保护功能,请妥善保管您的账户及密码信息。我们将通过向其它服务器备份、对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。</view> |
||||
<view class="text">(七)未成年人保护</view> |
||||
<view class="text">我们重视未成年人的个人信息保护,如您为未成年人,建议您请您的监护人阅读本隐私权条款,并在征得您的监护人同意的前提下使用我们的服务或向我们提供信息。</view> |
||||
<view class="text">六、免责条款</view> |
||||
<view class="text">1、平台用户通过平台获取信息服务的过程中需务必遵守中国的相关法律法规。平台不对用户达成协议过程中的任意纠纷承担法律责任。</view> |
||||
<view class="text">2、如买卖双方在交易过程中发生纠纷,在当事人自愿平等的前提下,买卖双方可提出要求平台协助调解。平台会在查明事实、分清是非的基础上,严格遵守国家法律法规来给出建议。不得因未经调解或者调解不成而阻止对方当事人向人民法院起诉。经调解达成的协议具有法律效力,但平台对此协议内容不承担任何法律责任。</view> |
||||
<view class="text">3、平台在此声明:对您使用本平台、与本平台相关的任何内容、服务或其它链接至本平台的站点、内容均不作直接、间接、法定、约定的保证;本平台对UGC(用户原创内容)的真实性不作保证,也不承担因其非真实而造成的任何责任。</view> |
||||
<view class="text">4、本站到第三方平台的链接仅作为一种方便服务提供给您。如果使用这些链接,您将离开本站。平台没有审查过任何第三方平台,对这些平台及其内容不进行控制,也不负任何责任。如果您决定访问任何与本站链接的第三方平台,其可能带来的结果和风险全部由您自己承担。</view> |
||||
<view class="text">5、用户应对使用平台得到的信息结果自行承担风险,我们仅作为服务平台,对本平台的使用即表明同意承担浏览本平台的全部风险,本平台对任何使用或提供本网站信息的商业活动及其风险不承担任何责任。用户自行发布的资源信息,我们不对信息内容的安全性、准确性、真实性、合法性负责,也不承担任何法律责任。</view> |
||||
<view class="text">6、本平台如因线路、硬件故障、系统维护、系统升级或其它不可抗力而导致暂停服务,于暂停服务期间造成的一切不便与损失,本平台不承担任何责任。</view> |
||||
<view class="text">7、本平台由于计算机黑客攻击、计算机病毒侵入、硬件设施损坏、或因政府行为、司法强制要求而造成个人资料泄露、丢失、被盗用或被篡改等,本平台不承担任何责任。</view> |
||||
<view class="text">8、因不可抗力因素或第三方支付平台的系统漏洞、故障造成交易环节中支付服务暂停或中断、不稳定的,本平台不承担任何责任。</view> |
||||
<view class="text">9、本平台上所有的增值服务或外包服务,是为了方便用户更好地使用本平台而提供,用户可自行选择接受与否,您应在接受增值服务或外包服务之前阅读相关协议,并按照您的需求和判断作出接受或不接受的意思表示。若您选择接受增值服务及外包服务的,视为您已经阅读相关协议,并自行承担接受增值服务或外包服务的风险,本平台不对任何提供给用户的增值服务及外包服务承担责任。</view> |
||||
<view class="text">10、任何单位或个人认为通过本平台网页内容可能涉嫌侵犯其知识产权,应该及时向我们提出书面权利通知,并提供身份证明、权属证明及详细侵权情况证明。我们收到上述法律文件后,将会依法尽快处理。</view> |
||||
<view class="text">七、法律管辖和适用</view> |
||||
<view class="text">任何有关本平台和本法律声明的争议、纠纷,均适用中华人民共和国法律。任何有关本平台和本法律声明的争议,应由有管辖权的人民法院管辖。如中华人民共和国法律的修改使上述任何条款成为非法,各方将同意由深圳智慧科技有限公司对上述条款作出修改。</view> |
||||
<view class="text">八、本声明的解释权及对本平台及软件使用的解释权归结于本公司。</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
|
||||
} |
||||
}, |
||||
methods: { |
||||
|
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.wrap { |
||||
padding: 30rpx; |
||||
.title { |
||||
margin-bottom: 30rpx; |
||||
font-size: 40rpx; |
||||
text-align: center; |
||||
} |
||||
.text { |
||||
font-size: 30rpx; |
||||
line-height: 1.6; |
||||
white-space: pre-wrap; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,317 @@ |
||||
<template> |
||||
<view class="page"> |
||||
<ul class="list"> |
||||
<uni-swipe-action> |
||||
<uni-swipe-action-item |
||||
v-for="(item, i) in list" |
||||
:key="i" |
||||
:threshold="0" |
||||
:right-options="delOption" |
||||
@click="del(item)" |
||||
> |
||||
<li> |
||||
<uni-data-checkbox v-if="item.check" class="check" multiple :value="[1]" :localdata="item.checkData" @change="e => checkChange(e, i)"></uni-data-checkbox> |
||||
<uni-data-checkbox v-else class="check" multiple v-model="item.check" :localdata="item.checkData" @change="e => checkChange(e, i)"></uni-data-checkbox> |
||||
<image class="icon" :src="$util.getIcon(item)"></image> |
||||
<view class="texts"> |
||||
<view class="name">{{ item.productName }}</view> |
||||
<view class="price">市场建议价:{{ item.marketUnitPrice }}元/年</view> |
||||
</view> |
||||
</li> |
||||
</uni-swipe-action-item> |
||||
</uni-swipe-action> |
||||
</ul> |
||||
<uni-load-more :status="status" /> |
||||
|
||||
<view class="btn-wrap"> |
||||
<uni-data-checkbox class="check" multiple v-model="checkAll" :localdata="checkAllData" @change="allChange"></uni-data-checkbox> |
||||
<view class="btns"> |
||||
<view class="btn del" @click="batchDel">删除</view> |
||||
<view class="btn" @click="submit">生成订单</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { shoppingCartList, delCart, detailsOfGoods } from '@/apis/modules/product.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
reachBottom: 0, // 是否是上拉加载。0->否,1->是,-1->加载完所有数据 |
||||
status: 'more', // 上拉加载状态 more|loading|noMore |
||||
searchTimer: null, |
||||
list: [], |
||||
page: 1, |
||||
pageSize: 10, |
||||
check: [1], |
||||
noCheck: [], |
||||
checkData: [{ |
||||
text: '', |
||||
value: 1 |
||||
}], |
||||
checkAll: [], |
||||
checkAllData: [{ |
||||
text: '全部', |
||||
value: 1 |
||||
}], |
||||
checked: [], // 已经勾选的集合 |
||||
delOption: [{ |
||||
text: '删除', |
||||
style: { |
||||
backgroundColor: '#F56C6C' |
||||
} |
||||
}], |
||||
} |
||||
}, |
||||
// 下拉刷新 |
||||
onPullDownRefresh() { |
||||
this.initList() |
||||
setTimeout(() => { |
||||
uni.stopPullDownRefresh() |
||||
}, 1500) |
||||
}, |
||||
// 上拉加载 |
||||
onReachBottom() { |
||||
if (this.reachBottom >= 0) { |
||||
this.reachBottom = 1 |
||||
this.status = 'loading' |
||||
this.getList() |
||||
} |
||||
}, |
||||
onShow() { |
||||
this.checked = [] |
||||
// 清除产品缓存 |
||||
try { |
||||
uni.removeStorageSync('orderForm') |
||||
uni.removeStorageSync('courses') |
||||
uni.removeStorageSync('orderEdited') |
||||
} catch (e) {} |
||||
this.initList() |
||||
}, |
||||
methods: { |
||||
// 购物车列表 |
||||
getList() { |
||||
uni.showLoading({ |
||||
title: '加载中' |
||||
}) |
||||
shoppingCartList({ |
||||
pageNum: this.page, |
||||
pageSize: this.pageSize, |
||||
}).then(({ data }) => { |
||||
const { records } = data |
||||
const all = this.checkAll.length // 是否勾选了全选 |
||||
const pageChange = this.reachBottom > 0 // 是否是翻页 |
||||
// 添加选择框字段 |
||||
records.forEach(e => { |
||||
e.check = 0 |
||||
e.checkData = [{ |
||||
text: '', |
||||
value: 1 |
||||
}] |
||||
}) |
||||
|
||||
// 未加载完所有数据,并且不是筛选,则拼接list,否则直接赋值 |
||||
this.list = pageChange ? [...this.list, ...records] : records |
||||
this.page++ // 每次获取了数据后page+1 |
||||
const noMore = this.list.length === data.total // 是否加载完所有数据 |
||||
this.status = noMore ? 'noMore' : 'more' // 加载完了则设置为noMore |
||||
this.reachBottom = noMore ? -1 : 0 // 加载完了则设置为-1 |
||||
uni.hideLoading() |
||||
}).catch(e => { |
||||
uni.hideLoading() |
||||
}) |
||||
}, |
||||
initList() { |
||||
this.page = 1 |
||||
this.reachBottom = 0 |
||||
this.getList() |
||||
}, |
||||
// 选择框回调 |
||||
checkChange(e, i) { |
||||
const { checked } = this |
||||
const item = this.list[i] |
||||
const { id } = item |
||||
const include = checked.findIndex(e => e.id === id) |
||||
// 选中的情况下,该产品如果没有push到已选数组里,则push |
||||
if (e.detail.value.length) { |
||||
include === -1 && checked.push(item) |
||||
} else { |
||||
// 取消选中的情况下,如果已选数组里存在该产品,则移除 |
||||
if (include !== -1) { |
||||
checked.splice(include, 1) |
||||
this.checkAll = [] |
||||
} |
||||
} |
||||
console.log(11, checked) |
||||
}, |
||||
// 全选 |
||||
allChange(e) { |
||||
const isCheck = !!e.detail.value.length // 是否选中 |
||||
const { list } = this |
||||
this.checked = isCheck ? JSON.parse(JSON.stringify(list)) : [] |
||||
list.forEach(e => { |
||||
e.check = isCheck ? 1 : 0 |
||||
}) |
||||
}, |
||||
// 生成产品参数 |
||||
createParam(e, authority, shopCartId) { |
||||
const { orderType } = this |
||||
const { mall, typeIds } = e |
||||
const trial = orderType == 2 // 是否是试用 |
||||
return { |
||||
dataOrCourseId: mall.associatedProduct, // id |
||||
mallId: mall.mallId, |
||||
productName: mall.productName, // 名称 |
||||
periodOfUse: '', // 使用期限 |
||||
startTime: this.$util.formatDate(new Date(), 'yyyy-MM-dd'), // 开始 |
||||
endTime: '', // 终止 |
||||
remainingPeriod: '', // 剩余期限 |
||||
marketValue: '', // 市场价 |
||||
marketPrice: mall.marketUnitPrice, // 市场单价 |
||||
finalPrice: 0, // 成交价 |
||||
finalValue: 0, // 成交单价(数据产品特有) |
||||
discountRate: '0%', // 折扣率 |
||||
accountNum: 1, // 账号数 |
||||
totalAmount: '', // 总价 |
||||
isEnable: 0, // 启用否:1启用,0禁用 |
||||
ship: 0, // 发货否(0未发货,1已发货,默认不发货) |
||||
authority, // 区分权限 0为数据平台权限,1为课程权限 |
||||
options: 2, |
||||
miniProgramPictureAddress: mall.appletIcon || '', // 图标 |
||||
settlementPrice: trial ? 0 : '', // 结算价 |
||||
settlementPriceUnit: 0, // 结算单价 |
||||
serviceFee: 0, // 平台服务费(前端计算后展示,不入库) |
||||
shopCartId, // 购物车id,订单提交后,调删除购物车的接口把这个产品删除 |
||||
typeId: typeIds && typeIds.length ? typeIds[0] : '', |
||||
} |
||||
}, |
||||
// 删除 |
||||
del(e) { |
||||
const that = this |
||||
uni.showModal({ |
||||
title: '提示', |
||||
content: '确定要删除吗?', |
||||
success(res) { |
||||
if (res.confirm) { |
||||
delCart([e.id]).then(res => { |
||||
that.initList() |
||||
}).catch(e => {}) |
||||
} |
||||
} |
||||
}) |
||||
}, |
||||
// 批量删除 |
||||
batchDel() { |
||||
const list = this.checked // 已选产品 |
||||
if (list.length) { |
||||
const that = this |
||||
uni.showModal({ |
||||
title: '提示', |
||||
content: '确定要删除吗?', |
||||
success(res) { |
||||
if (res.confirm) { |
||||
delCart(list.map(e => e.id)).then(res => { |
||||
that.checkAll = [] |
||||
that.checked = [] |
||||
that.initList() |
||||
}).catch(e => {}) |
||||
} |
||||
} |
||||
}) |
||||
} else { |
||||
this.$util.errMsg('请选择产品!') |
||||
} |
||||
}, |
||||
// 确定 |
||||
submit() { |
||||
const list = this.checked // 已选产品 |
||||
if (list.length) { |
||||
// 判断勾选的产品是否有重复的 |
||||
if (new Set(list.map(e => e.mallId)).size !== list.length) return this.$util.errMsg('所选产品存在重复,请重新选择') |
||||
|
||||
const promises = [] |
||||
let courses = [] |
||||
list.forEach(e => { |
||||
promises.push(new Promise(async (resolve, reject) => { |
||||
// 查询产品详情 |
||||
const res = await detailsOfGoods(e.mallId) |
||||
const n = res.orderDetails |
||||
courses.push(this.createParam(n, this.$util.getOrderType(n.classificationIds[0]), e.id)) |
||||
resolve() |
||||
})) |
||||
}) |
||||
Promise.all(promises).then(_ => { |
||||
uni.setStorageSync('courses', courses) |
||||
this.$util.to(`../orderDetail/orderDetail?shopCart=1`) |
||||
}) |
||||
} else { |
||||
this.$util.errMsg('请先添加产品!') |
||||
} |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.page { |
||||
padding-bottom: 130rpx; |
||||
} |
||||
.list { |
||||
li { |
||||
display: flex; |
||||
align-items: center; |
||||
width: 100%; |
||||
padding: 30rpx 24rpx; |
||||
margin: 16rpx 24rpx; |
||||
background-color: #fff; |
||||
border-radius: 16rpx; |
||||
box-sizing: border-box; |
||||
} |
||||
.name { |
||||
margin-bottom: 10rpx; |
||||
font-size: 30rpx; |
||||
color: #333; |
||||
} |
||||
.price { |
||||
font-size: 26rpx; |
||||
color: #333; |
||||
} |
||||
.icon { |
||||
width: 100rpx; |
||||
min-width: 100rpx; |
||||
height: 100rpx; |
||||
margin: 0 20rpx; |
||||
border-radius: 4px; |
||||
} |
||||
} |
||||
/deep/.check { |
||||
.checklist-box { |
||||
margin: 0 !important; |
||||
} |
||||
.checkbox__inner { |
||||
width: 40rpx !important; |
||||
height: 40rpx !important; |
||||
border-radius: 50% !important; |
||||
} |
||||
.checkbox__inner-icon { |
||||
top: 8rpx !important; |
||||
left: 14rpx !important; |
||||
} |
||||
} |
||||
.btn-wrap { |
||||
position: fixed; |
||||
justify-content: space-between; |
||||
.btns { |
||||
display: inline-flex; |
||||
} |
||||
.btn { |
||||
width: auto; |
||||
padding: 0 50rpx; |
||||
} |
||||
.del { |
||||
margin-right: 20rpx; |
||||
background-color: #b5b5b5; |
||||
} |
||||
} |
||||
</style> |
@ -1,218 +0,0 @@ |
||||
<template> |
||||
<view> |
||||
<view class="filter"> |
||||
<uni-search-bar class="search" radius="30" placeholder="请输入产品名称" v-model="keyword" clearButton="auto" cancelButton="none" /> |
||||
<view :class="['sort', sort]" @click="switchSort"></view> |
||||
</view> |
||||
|
||||
<ul class="tab"> |
||||
<li v-for="(tab, i) in tabs" :class="{active: curTab === tab.id}" @click="tabChange(tab)">{{ tab.name }}</li> |
||||
</ul> |
||||
|
||||
<ul class="list"> |
||||
<li v-for="item in list"> |
||||
<view class="pro-name"> |
||||
<image class="icon" :src="$util.getIcon(item)" mode="widthFix"></image> |
||||
{{ item.productName }} |
||||
</view> |
||||
<view class="info"> |
||||
<view class="line"> |
||||
<text class="name">产品简介:</text> |
||||
<view class="val ell-wrap"> |
||||
<view :class="{ell: !item.toggle}">{{ item.briefIntroduction }}</view> |
||||
<view v-if="item.briefIntroduction.length > 14" class="toggle" @click="toggle(item)">{{ item.toggle ? '收起' : '展开' }}</view> |
||||
</view> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">产品类型:</text> |
||||
<text class="val">{{ tabs.find(e => e.id === item.productType).name }}</text> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">市场单价:</text> |
||||
<text class="val">{{ item.marketPrice }}元/年</text> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">结算方式:</text> |
||||
<text class="val">{{ item.settlementMethod ? '比例分成' : '结算单价' }} {{ item.settlementPrice }}元/年</text> |
||||
</view> |
||||
<view class="line"> |
||||
<text class="name">供应厂商:</text> |
||||
<text class="val">{{ item.supplierName }}</text> |
||||
</view> |
||||
</view> |
||||
</li> |
||||
</ul> |
||||
<uni-load-more :status="status" /> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { AppletsDataProductList } from '@/apis/modules/product.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
curTab: '', |
||||
tabs: [ |
||||
{ |
||||
name: '全部', |
||||
id: '' |
||||
}, |
||||
{ |
||||
name: '实训课程', |
||||
id: 1 |
||||
}, |
||||
{ |
||||
name: '理论课程', |
||||
id: 0 |
||||
}, |
||||
{ |
||||
name: '数据产品', |
||||
id: 2 |
||||
} |
||||
], |
||||
reachBottom: 0, // 是否是上拉加载。0->否,1->是,-1->加载完所有数据 |
||||
status: 'more', // 上拉加载状态 more|loading|noMore |
||||
searchTimer: null, |
||||
sort: 'desc', |
||||
keyword: '', |
||||
list: [], |
||||
page: 1, |
||||
pageSize: 10 |
||||
} |
||||
}, |
||||
watch: { |
||||
keyword () { |
||||
clearTimeout(this.searchTimer) |
||||
this.searchTimer = setTimeout(() => { |
||||
this.initList() |
||||
}, 500) |
||||
} |
||||
}, |
||||
// 下拉刷新 |
||||
onPullDownRefresh() { |
||||
this.initList() |
||||
setTimeout(() => { |
||||
uni.stopPullDownRefresh() |
||||
}, 1500) |
||||
}, |
||||
// 上拉加载 |
||||
onReachBottom() { |
||||
if (this.reachBottom >= 0) { |
||||
this.reachBottom = 1 |
||||
this.status = 'loading' |
||||
this.getList() |
||||
} |
||||
}, |
||||
onShow() { |
||||
this.initList() |
||||
}, |
||||
methods: { |
||||
getList() { |
||||
AppletsDataProductList({ |
||||
sort: this.sort, |
||||
keywords: this.keyword, |
||||
productType: this.curTab, |
||||
pageNum: this.page, |
||||
pageSize: this.pageSize |
||||
}).then(({ data }) => { |
||||
// 未加载完所有数据,并且不是筛选,则拼接list,否则直接赋值 |
||||
const list = data.records |
||||
list.map(e => { |
||||
e.toggle = e.briefIntroduction.length < 14 // 超过了14个字才需要显示展开按钮 |
||||
}) |
||||
this.list = this.reachBottom > 0 ? [...this.list, ...list] : list |
||||
this.page++ // 每次获取了数据后page+1 |
||||
const noMore = this.list.length === data.total // 是否加载完所有数据 |
||||
this.status = noMore ? 'noMore' : 'more' // 加载完了则设置为noMore |
||||
this.reachBottom = noMore ? -1 : 0 // 加载完了则设置为-1 |
||||
}).catch(e => {}) |
||||
}, |
||||
initList() { |
||||
this.page = 1 |
||||
this.reachBottom = 0 |
||||
this.getList() |
||||
}, |
||||
// 展开 |
||||
toggle(item) { |
||||
item.toggle = !item.toggle |
||||
}, |
||||
// 排序 |
||||
switchSort() { |
||||
this.sort = this.sort === 'desc' ? 'asc' : 'desc' |
||||
this.initList() |
||||
}, |
||||
// tab切换 |
||||
tabChange(tab) { |
||||
this.curTab = tab.id |
||||
this.initList() |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.filter { |
||||
display: flex; |
||||
align-items: center; |
||||
.search { |
||||
flex: 1; |
||||
} |
||||
.sl-filter { |
||||
width: 30%; |
||||
margin-left: 10%; |
||||
} |
||||
} |
||||
.list { |
||||
li { |
||||
padding: 0 24rpx; |
||||
margin: 16rpx 24rpx; |
||||
background-color: #fff; |
||||
border-radius: 16rpx; |
||||
} |
||||
.pro-name { |
||||
display: flex; |
||||
align-items: center; |
||||
padding: 18rpx 0; |
||||
font-size: 30rpx; |
||||
color: #333; |
||||
border-bottom: 1px solid #E6E8ED; |
||||
.icon { |
||||
width: 52rpx; |
||||
height: 52rpx; |
||||
margin-right: 20rpx; |
||||
} |
||||
} |
||||
.info { |
||||
padding: 12rpx 0; |
||||
} |
||||
.line { |
||||
display: flex; |
||||
padding: 12rpx 0; |
||||
} |
||||
.name { |
||||
margin-right: 10rpx; |
||||
font-size: 28rpx; |
||||
color: #999; |
||||
} |
||||
.val { |
||||
max-width: 70%; |
||||
font-size: 28rpx; |
||||
color: #333; |
||||
} |
||||
.ell-wrap { |
||||
display: inline-flex; |
||||
align-items: center; |
||||
} |
||||
.ell { |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
} |
||||
.toggle { |
||||
margin-left: 10rpx; |
||||
white-space: nowrap; |
||||
font-size: 24rpx; |
||||
color: #0e92ef; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,530 @@ |
||||
<template> |
||||
<view :class="['page', {oh: !per}]"> |
||||
<view class="status-bar"></view> |
||||
<image class="bg" src="@/static/image/workbench/index2.png" mode="widthFix"></image> |
||||
<view class="team-wrap" :style="{paddingTop: headerTop}"> |
||||
<view class="team"> |
||||
<uni-data-picker class="picker-input" placeholder="切换团队" popup-title="切换团队" preload :clear-icon="false" :localdata="list" :map="{text: 'partnerClassificationName', value: 'teamId'}" v-model="teamId" @change="teamChange"></uni-data-picker> |
||||
</view> |
||||
</view> |
||||
|
||||
<view class="banner"> |
||||
<image class="img" src="@/static/image/workbench/index1.png" mode="widthFix"></image> |
||||
<view class="info"> |
||||
<view class="title">城市合伙人招募中</view> |
||||
<view class="text">携手共创教育信息化新未来,合伙共享产业互联领域新红利</view> |
||||
</view> |
||||
</view> |
||||
|
||||
<ul class="entry"> |
||||
<li @click="toModule('client')"> |
||||
<image class="icon" src="@/static/image/workbench/index3.png" mode="widthFix"></image> |
||||
<view class="text">客户</view> |
||||
</li> |
||||
<li @click="toModule('plan')"> |
||||
<image class="icon" src="@/static/image/workbench/index4.png" mode="widthFix"></image> |
||||
<view class="text">方案</view> |
||||
</li> |
||||
<li @click="toModule('study')"> |
||||
<image class="icon" src="@/static/image/workbench/index5.png" mode="widthFix"></image> |
||||
<view class="text">学习</view> |
||||
</li> |
||||
<li @click="toModule('info')"> |
||||
<image class="icon" src="@/static/image/workbench/index6.png" mode="widthFix"></image> |
||||
<view class="text">资讯</view> |
||||
</li> |
||||
</ul> |
||||
|
||||
<view class="panel study-panel"> |
||||
<view class="title" @click="toStudy"> |
||||
<view class="left"> |
||||
<image class="icon" src="@/static/image/workbench/index8.png" mode="widthFix"></image> |
||||
学习速递 |
||||
</view> |
||||
<view class="right"> |
||||
<text>全部</text> |
||||
<image class="arrow" src="@/static/image/workbench/index10.png" mode="widthFix"></image> |
||||
</view> |
||||
</view> |
||||
<view class="study"> |
||||
<scroll-view class="scroll" scroll-x="true"> |
||||
<view v-for="(item, i) in studies" :key="i" class="item" @click="toDetail(item)"> |
||||
<image class="pic" :src="item.bannerImg"></image> |
||||
<view class="text">{{ item.title }}</view> |
||||
</view> |
||||
</scroll-view> |
||||
</view> |
||||
</view> |
||||
|
||||
<view class="panel"> |
||||
<view class="title" @click="toPanel(1)"> |
||||
<view class="left"> |
||||
<image class="icon" src="@/static/image/workbench/index8.png" mode="widthFix"></image> |
||||
销售进展 |
||||
</view> |
||||
<view class="right"> |
||||
<image class="date" src="@/static/image/workbench/index7.png" mode="widthFix"></image> |
||||
<text>本月</text> |
||||
<image class="arrow" src="@/static/image/workbench/index10.png" mode="widthFix"></image> |
||||
</view> |
||||
</view> |
||||
<view class="data first"> |
||||
<view class="line"> |
||||
<view class="item"> |
||||
<view class="name">本月新增试用客户</view> |
||||
<view class="val">{{ sell.trialUser || 0 }}</view> |
||||
</view> |
||||
<view class="item"> |
||||
<view class="name">本月成单客户</view> |
||||
<view class="val">{{ sell.regularUser || 0 }}</view> |
||||
</view> |
||||
</view> |
||||
<view class="line"> |
||||
<view class="item"> |
||||
<view class="name">本月新增产品试用</view> |
||||
<view class="val">{{ sell.trialProduct || 0 }}</view> |
||||
</view> |
||||
<view class="item"> |
||||
<view class="name">本月成交订单产品</view> |
||||
<view class="val">{{ sell.officialProduct || 0 }}</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
|
||||
|
||||
<view class="panel"> |
||||
<view class="title y-title" @click="toPanel(1)"> |
||||
<view class="left"> |
||||
<image class="icon" src="@/static/image/workbench/index9.png" mode="widthFix"></image> |
||||
年度经营分析 |
||||
</view> |
||||
<image class="arrow" src="@/static/image/workbench/index10.png" mode="widthFix"></image> |
||||
</view> |
||||
<view class="data second"> |
||||
<view class="line"> |
||||
<view class="item item1"> |
||||
<view class="val">{{ handleNum(analysis.finalPrice) }}</view> |
||||
<view class="name">总成交金额</view> |
||||
</view> |
||||
<view class="item item2"> |
||||
<view class="val">{{ handleNum(analysis.settlementPrice) }}</view> |
||||
<view class="name">总结算金额</view> |
||||
</view> |
||||
<view class="item equal"> |
||||
<view class="val">{{ handleNum(analysis.marketingServiceCharge) }}</view> |
||||
<view class="name">市场服务费</view> |
||||
</view> |
||||
<view class="item"> |
||||
<view class="val">{{ handleNum(analysis.projectBenefit) }}</view> |
||||
<view class="name">项目收益</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
<view v-if="!per" class="per-mask">功能升级中,敬请期待...</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { getUserRolesPermissionMenu } from '@/apis/modules/user.js' |
||||
import { getTeamsByAccountId, getTheBusinessManagerIdsUnderTheTeam } from '@/apis/modules/client.js' |
||||
import { treeList, salesProgress, annualOperatingAnalysis } from '@/apis/modules/parner.js' |
||||
import { partnerOperatingList } from '@/apis/modules/article.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
per: true, // 是否有权限 |
||||
teamId: uni.getStorageSync('teamId') || '', |
||||
list: [], |
||||
id: '', |
||||
teamList: [], |
||||
studies: [], |
||||
sell: {}, |
||||
analysis: { |
||||
finalPrice: 0, |
||||
settlementPrice: 0, |
||||
marketingServiceCharge: 0, |
||||
projectBenefit: 0, |
||||
}, |
||||
headerTop: '8px', |
||||
} |
||||
}, |
||||
onShow() { |
||||
// #ifdef MP-WEIXIN |
||||
this.headerTop = uni.getMenuButtonBoundingClientRect().top + 8 + 'px' |
||||
// #endif |
||||
this.per = true |
||||
|
||||
if (uni.getStorageSync('token')) { |
||||
this.getInfo() |
||||
} else { |
||||
uni.redirectTo({ |
||||
url: '/pages/login/login' |
||||
}) |
||||
} |
||||
}, |
||||
methods: { |
||||
// 初始化权限 |
||||
initRole() { |
||||
if (!uni.getStorageSync('auth').includes('工作台')) { |
||||
this.per = false |
||||
} |
||||
this.getStudy() |
||||
this.getSell() |
||||
this.getAnalysis() |
||||
uni.hideLoading() |
||||
}, |
||||
// 查询当前角色权限 |
||||
getAuth() { |
||||
getUserRolesPermissionMenu({ |
||||
teamId: this.list.find(e => e.teamId == this.teamId).partnerClassificationId, |
||||
platformId: 4 |
||||
}).then(({ permissionMenu }) => { |
||||
uni.hideLoading() |
||||
const auth = [] |
||||
// 生成权限数组 |
||||
const generateAuth = (list, parent) => { |
||||
list.map(e => { |
||||
const name = `${parent ? parent + ':' : ''}${e.name}` |
||||
auth.push(name) |
||||
generateAuth(e.children, name) |
||||
}) |
||||
} |
||||
generateAuth(permissionMenu[0].children, '') |
||||
uni.setStorageSync('auth', auth) |
||||
this.$forceUpdate() |
||||
this.initRole() |
||||
}).catch(e => { |
||||
uni.hideLoading() |
||||
uni.setStorageSync('auth', []) |
||||
this.initRole() |
||||
}) |
||||
}, |
||||
// 获取团队 |
||||
getInfo() { |
||||
uni.showLoading({ |
||||
title: '加载中' |
||||
}) |
||||
getTeamsByAccountId().then(({ data }) => { |
||||
data.map(e => { |
||||
const n = e.partnerClassificationList |
||||
e.id = n.id |
||||
// parnerId是商务经理id,teamId则是下面这个,其他地方要用的话直接uni.getStorageSync('team').partnerId去使用 |
||||
e.teamId = e.isTeam == 1 ? +e.partnerClassificationId : n.id |
||||
e.partnerClassificationName = n.partnerClassificationName |
||||
delete e.partnerClassificationList |
||||
}) |
||||
if (data.length) { |
||||
/** |
||||
* @description 如果是第一次进,则默认选中第一个团队,并把该团队的信息存入缓存 |
||||
* 或者团队列表里没有该id,则说明超管已经被转让,也需要重新选中团队 |
||||
*/ |
||||
if (!uni.getStorageSync('team') || !data.find(e => e.teamId == this.teamId)) { |
||||
this.teamId = data[0].teamId |
||||
uni.setStorageSync('teamId', data[0].teamId) |
||||
uni.setStorageSync('team', data[0]) |
||||
} |
||||
} else { |
||||
// 如果没有团队,则退出登录 |
||||
uni.hideLoading() |
||||
uni.clearStorageSync() |
||||
uni.redirectTo({ |
||||
url: '../index/index' |
||||
}) |
||||
} |
||||
|
||||
this.list = data |
||||
this.getAuth() |
||||
}).catch(e => { |
||||
uni.hideLoading() |
||||
}) |
||||
}, |
||||
// 学习速递 |
||||
getStudy() { |
||||
partnerOperatingList({ |
||||
topSort: 'desc', |
||||
pageNum: 1, |
||||
pageSize: 5, |
||||
querySource: 4, |
||||
typeId: 1, |
||||
}).then(({ page }) => { |
||||
this.studies = page |
||||
}).catch(e => {}) |
||||
}, |
||||
// 跳转学习 |
||||
toStudy() { |
||||
this.$util.to('/team/study/study') |
||||
}, |
||||
// 跳转学习详情 |
||||
toDetail(item) { |
||||
this.$util.to(`/team/article/article?id=` + item.id) |
||||
}, |
||||
// 获取销售进展 |
||||
getSell() { |
||||
salesProgress({ |
||||
businessManagerId: uni.getStorageSync('team').partnerId, |
||||
teamId: this.teamId, |
||||
}).then(res => { |
||||
this.sell = res |
||||
}).catch(e => {}) |
||||
}, |
||||
// 处理年度经营分析数值 除以10000 + w |
||||
handleNum(num) { |
||||
// return num ? parseInt(num / 10000) + 'w' : 0 |
||||
return num |
||||
}, |
||||
// 获取年度经营分析 |
||||
getAnalysis() { |
||||
annualOperatingAnalysis({ |
||||
businessManagerId: uni.getStorageSync('team').partnerId, |
||||
teamId: this.teamId, |
||||
}).then(({ data }) => { |
||||
if (data) this.analysis = data |
||||
}).catch(e => {}) |
||||
}, |
||||
// 团队选择回调 |
||||
teamChange() { |
||||
const { teamId } = this |
||||
const e = this.list.find(e => e.teamId == teamId) |
||||
uni.setStorageSync('team', e) |
||||
uni.setStorageSync('teamId', teamId) |
||||
this.getAuth() |
||||
}, |
||||
// 模块点击 |
||||
toModule(i) { |
||||
this.$uma.trackEvent(i) // 友盟统计 |
||||
let path = '/order/clients/clients' |
||||
if (i === 'plan') { |
||||
path = '/team/plans/plans' |
||||
} else if (i === 'study') { |
||||
path = '/team/study/study' |
||||
} else if (i === 'info') { |
||||
path = '/team/info/info' |
||||
} |
||||
this.$util.to(path, { type: i }) |
||||
}, |
||||
// 提示暂未开放 |
||||
toPanel(i) { |
||||
this.$util.errMsg('功能暂未开放!') |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.page { |
||||
position: relative; |
||||
min-height: 100%; |
||||
padding: 0 22rpx 30rpx; |
||||
box-sizing: border-box; |
||||
} |
||||
.status-bar { |
||||
width: 100%; |
||||
// height: calc(var(--status-bar-height)); |
||||
} |
||||
.bg { |
||||
z-index: -1; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
.team { |
||||
position: relative; |
||||
width: 300rpx; |
||||
margin-bottom: 30rpx; |
||||
/deep/.selected-item text { |
||||
font-size: 30rpx; |
||||
font-weight: 600; |
||||
color: #5f5f5f; |
||||
} |
||||
} |
||||
.banner { |
||||
position: relative; |
||||
.img { |
||||
width: 100%; |
||||
} |
||||
.info { |
||||
position: absolute; |
||||
top: 80rpx; |
||||
left: 46rpx; |
||||
} |
||||
.title { |
||||
margin-bottom: 15rpx; |
||||
font-size: 36rpx; |
||||
font-weight: 600; |
||||
color: #001D67; |
||||
} |
||||
.text { |
||||
font-size: 20rpx; |
||||
font-family: PingFangSC-Regular, PingFang SC; |
||||
color: #001D67; |
||||
} |
||||
} |
||||
.entry { |
||||
display: flex; |
||||
justify-content: space-around; |
||||
align-items: center; |
||||
margin: 10rpx 0 30rpx; |
||||
text-align: center; |
||||
.icon { |
||||
width: 78rpx; |
||||
} |
||||
.info-icon { |
||||
width: 64rpx; |
||||
max-height: 64rpx; |
||||
} |
||||
.text { |
||||
font-size: 28rpx; |
||||
color: #333; |
||||
} |
||||
} |
||||
.panel { |
||||
margin: 20rpx 10rpx; |
||||
background-color: #fff; |
||||
border-radius: 20rpx; |
||||
overflow: hidden; |
||||
&.study-panel { |
||||
background-color: transparent; |
||||
} |
||||
.title { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
padding: 17rpx 20rpx; |
||||
background: linear-gradient(90deg, #E5EFFF 0%, #FFFFFF 100%); |
||||
} |
||||
.y-title { |
||||
background: linear-gradient(90deg, #FFF5E5 0%, #FFFFFF 100%); |
||||
} |
||||
.left { |
||||
display: flex; |
||||
align-items: center; |
||||
font-size: 30rpx; |
||||
color: #333; |
||||
} |
||||
.right { |
||||
display: flex; |
||||
align-items: center; |
||||
font-size: 28rpx; |
||||
color: #333; |
||||
text { |
||||
margin: 0 15rpx 0 8rpx; |
||||
} |
||||
} |
||||
.icon { |
||||
width: 36rpx; |
||||
margin-right: 10rpx; |
||||
} |
||||
.date { |
||||
width: 26rpx; |
||||
} |
||||
.arrow { |
||||
width: 16rpx; |
||||
} |
||||
.data { |
||||
padding: 33rpx 36rpx; |
||||
} |
||||
.line { |
||||
display: flex; |
||||
} |
||||
.first { |
||||
.item:first-child { |
||||
width: 65%; |
||||
} |
||||
.name { |
||||
margin-bottom: 14rpx; |
||||
font-size: 24rpx; |
||||
color: #999; |
||||
} |
||||
.val { |
||||
font-size: 30rpx; |
||||
color: #333; |
||||
} |
||||
.line:first-child { |
||||
margin-bottom: 32rpx; |
||||
} |
||||
} |
||||
.second { |
||||
padding: 38rpx 10rpx; |
||||
.item { |
||||
position: relative; |
||||
text-align: center; |
||||
&:after { |
||||
content: ''; |
||||
position: absolute; |
||||
bottom: 14rpx; |
||||
right: -35rpx; |
||||
width: 22rpx; |
||||
height: 2px; |
||||
background-color: #ccc; |
||||
} |
||||
&.item1 { |
||||
margin-right: 53rpx; |
||||
} |
||||
&.item2 { |
||||
margin-right: 54rpx; |
||||
} |
||||
&.equal { |
||||
margin-right: 50rpx; |
||||
&:after { |
||||
bottom: 6rpx; |
||||
right: -39rpx; |
||||
height: 2px; |
||||
padding-bottom: 5px; |
||||
border-bottom: 2px solid #ccc; |
||||
background-clip:content-box; |
||||
box-sizing: content-box; |
||||
} |
||||
} |
||||
&:last-child:after { |
||||
display: none; |
||||
} |
||||
} |
||||
.val { |
||||
margin-bottom: 10rpx; |
||||
font-size: 30rpx; |
||||
color: #333; |
||||
} |
||||
.name { |
||||
font-size: 24rpx; |
||||
color: #999; |
||||
} |
||||
} |
||||
.study { |
||||
width: 100%; |
||||
margin-top: 15rpx; |
||||
overflow: auto; |
||||
.item { |
||||
display: inline-block; |
||||
width: 280rpx; |
||||
margin-right: 20rpx; |
||||
background-color: #fff; |
||||
border-radius: 8px; |
||||
overflow: hidden; |
||||
} |
||||
.pic { |
||||
width: 100%; |
||||
height: 130rpx; |
||||
} |
||||
.text { |
||||
padding: 15rpx; |
||||
font-size: 28rpx; |
||||
color: #333; |
||||
text-align: center; |
||||
white-space: nowrap; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
} |
||||
} |
||||
.scroll { |
||||
width: 100%; |
||||
white-space: nowrap; |
||||
} |
||||
} |
||||
.oh { |
||||
overflow: hidden; |
||||
} |
||||
</style> |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 775 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 599 B |
After Width: | Height: | Size: 439 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 518 B |
After Width: | Height: | Size: 522 B |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 230 B |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 840 B |
Before Width: | Height: | Size: 319 B |
Before Width: | Height: | Size: 727 B |
Before Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 844 B |
Before Width: | Height: | Size: 672 B |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 528 B |
Before Width: | Height: | Size: 654 B After Width: | Height: | Size: 620 B |
After Width: | Height: | Size: 382 B |