当前位置: 首页 > news >正文

专门做书籍设计的网站优化网站平台

专门做书籍设计的网站,优化网站平台,自建站费用,如何申请一个网站 做视频直播原框架代码: 赵志江/huzhushan-vue3-element-admin 目录 TagsBar实现 实现同一个菜单多标签 device/detail/:id&#xff0c;不同参数时页面缓存删不掉的问题 TagsBar实现 在src/layout/components/下新建目录Tagsbar&#xff0c;新建index.vue <template><div c…

原框架代码: 赵志江/huzhushan-vue3-element-admin

目录

TagsBar实现

实现同一个菜单多标签

device/detail/:id,不同参数时页面缓存删不掉的问题


TagsBar实现

在src/layout/components/下新建目录Tagsbar,新建index.vue

<template><div class="tags-container" :class="{ hide: !isTagsbarShow }"><el-scrollbarref="scrollContainer":vertical="false"class="scroll-container"@wheel.prevent="onScroll"><router-linkv-for="(tag, i) in tagList":key="tag.fullPath":to="tag":ref="el => setItemRef(i, el)"customv-slot="{ navigate, isExactActive }"><divclass="tags-item":class="isExactActive? 'active' : ''"@click="navigate"@click.middle="closeTag(tag)"@contextmenu.prevent="openMenu(tag, $event)"><span class="title">{{ $t(tag.title) }}</span><el-iconv-if="!isAffix(tag)"class="el-icon-close"@click.prevent.stop="closeTag(tag)"><Close /></el-icon></div></router-link></el-scrollbar></div><ulv-show="visible":style="{ left: left + 'px', top: top + 'px' }"class="contextmenu"><!-- <li @click="refreshSelectedTag(selectedTag)">{{ $t('tags.refresh') }}</li> --><li v-if="!isAffix(selectedTag)" @click="closeTag(selectedTag)">{{ $t('tags.close') }}</li><li @click="closeOtherTags">{{ $t('tags.other') }}</li><li @click="closeLeftTags">{{ $t('tags.left') }}</li><li @click="closeRightTags">{{ $t('tags.right') }}</li><li @click="closeAllTags">{{ $t('tags.all') }}</li></ul>
</template><script>
import { defineComponent, computed, getCurrentInstance } from 'vue'
import { useTags } from './hooks/useTags'
import { useContextMenu } from './hooks/useContextMenu'
import { useLayoutsettings } from '@/pinia/modules/layoutSettings'export default defineComponent({name: 'Tagsbar',mounted() {},setup() {const instance = getCurrentInstance()instance.appContext.config.globalProperties.$tagsbar = thisconst defaultSettings = useLayoutsettings()const isTagsbarShow = computed(() => defaultSettings.tagsbar.isShow)const tags = useTags()const contextMenu = useContextMenu(tags.tagList)const onScroll = e => {tags.handleScroll(e)contextMenu.closeMenu.value()}return {isTagsbarShow,onScroll,...tags,...contextMenu}},
})
</script><style lang="scss" scoped>
.tags-container {height: 32px;width: 100%;background: #fff;border-bottom: 1px solid #e0e4ef;&.hide {display: none;}.scroll-container {white-space: nowrap;overflow: hidden;::v-deep(.el-scrollbar__bar) {bottom: 0px;}}.tags-item {display: inline-block;height: 32px;line-height: 32px;box-sizing: border-box;border-left: 1px solid #e6e6e6;border-right: 1px solid #e6e6e6;color: #5c5c5c;background: #fff;padding: 0 8px;font-size: 12px;margin-left: -1px;vertical-align: bottom;cursor: pointer;&:first-of-type {margin-left: 15px;}&:last-of-type {margin-right: 15px;}&.active {color: #303133;background: #f5f5f5;}.title {display: inline-block;vertical-align: top;max-width: 200px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}.el-icon-close {color: #5c5c5c;margin-left: 8px;width: 16px;height: 16px;vertical-align: -2px;border-radius: 50%;text-align: center;transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);transform-origin: 100% 50%;&:before {transform: scale(0.8);display: inline-block;vertical-align: -2px;}&:hover {background-color: #333;color: #fff;}}}
}
.contextmenu {margin: 0;background: #fff;z-index: 3000;position: fixed;list-style-type: none;padding: 5px 0;border-radius: 4px;font-size: 12px;font-weight: 400;color: #333;box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);white-space: nowrap;li {margin: 0;padding: 8px 16px;cursor: pointer;&:hover {background: #eee;}}
}
</style>

新建hooks目录,新建useTags.js

import { storeToRefs } from 'pinia'
import { useTags as useTagsbar } from '@/pinia/modules/tags'
import { useScrollbar } from './useScrollbar'
import { watch, computed, ref, nextTick, onBeforeMount } from 'vue'
import { useRouter } from 'vue-router'export const isAffix = tag => {return !!tag.meta && !!tag.meta.affix
}export const useTags = () => {const tagStore = useTagsbar()const { tagList } = storeToRefs(tagStore)const { addTag, delTag, saveActivePosition, updateTagList } = tagStoreconst router = useRouter()const route = router.currentRouteconst routes = computed(() => router.getRoutes())const tagsItem = ref([])const setItemRef = (i, el) => {tagsItem.value[i] = el}const scrollbar = useScrollbar(tagsItem)watch(() => tagList.value.length,() => {tagsItem.value = []})const filterAffixTags = routes => {return routes.filter(route => isAffix(route))}const initTags = () => {const affixTags = filterAffixTags(routes.value)for (const tag of affixTags) {if (tag.name) {addTag(tag)}}// 不在路由中的所有标签,需要删除const noUseTags = tagList.value.filter(tag =>routes.value.every(route => route.name !== tag.name))noUseTags.forEach(tag => {delTag(tag)})}const addTagList = () => {const tag = route.valueif (!!tag.name && tag.matched[0].components.default.name === 'layout') {addTag(tag)}}const saveTagPosition = tag => {const index = tagList.value.findIndex(item => item.fullPath === tag.fullPath)saveActivePosition(Math.max(0, index))}const moveToCurrentTag = () => {nextTick(() => {for (const tag of tagsItem.value) {if (!!tag && tag.to.path === route.value.path) {scrollbar.moveToTarget(tag)if (tag.to.fullPath !== route.value.fullPath) {updateTagList(route.value)}break}}})}onBeforeMount(() => {initTags()addTagList()moveToCurrentTag()})watch(route, (newRoute, oldRoute) => {saveTagPosition(oldRoute) // 保存标签的位置addTagList()moveToCurrentTag()})return {tagList,setItemRef,isAffix,...scrollbar,}
}

useScrollbar.js

import { ref } from 'vue'export const useScrollbar = tagsItem => {const scrollContainer = ref(null)const scrollLeft = ref(0)const doScroll = val => {scrollLeft.value = valscrollContainer.value.setScrollLeft(scrollLeft.value)}const handleScroll = e => {const $wrap = scrollContainer.value.wrapRefif ($wrap.offsetWidth + scrollLeft.value > $wrap.children[0].scrollWidth) {doScroll($wrap.children[0].scrollWidth - $wrap.offsetWidth)return} else if (scrollLeft.value < 0) {doScroll(0)return}const eventDelta = e.wheelDelta || -e.deltaYdoScroll(scrollLeft.value - eventDelta / 4)}const moveToTarget = currentTag => {const $wrap = scrollContainer.value.wrapRefconst tagList = tagsItem.valuelet firstTag = nulllet lastTag = nullif (tagList.length > 0) {firstTag = tagList[0]lastTag = tagList[tagList.length - 1]}if (firstTag === currentTag) {doScroll(0)} else if (lastTag === currentTag) {doScroll($wrap.children[0].scrollWidth - $wrap.offsetWidth)} else {const el = currentTag.$el.nextElementSiblingel.offsetLeft + el.offsetWidth > $wrap.offsetWidth? doScroll(el.offsetLeft - el.offsetWidth): doScroll(0)}}return {scrollContainer,handleScroll,moveToTarget,}
}

useContextMenu.js

import { useTags } from '@/pinia/modules/tags'
import { onMounted, onBeforeUnmount, reactive, toRefs, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { isAffix } from './useTags'export const useContextMenu = tagList => {const router = useRouter()const route = useRoute()const tagsStore = useTags()const state = reactive({visible: false,top: 0,left: 0,selectedTag: {},openMenu(tag, e) {state.visible = truestate.left = e.clientXstate.top = e.clientYstate.selectedTag = tag},closeMenu() {state.visible = false},refreshSelectedTag(tag) {tagsStore.deCacheList(tag)const { fullPath } = tagnextTick(() => {router.replace({path: '/redirect' + fullPath,})})},closeTag(tag) {if (isAffix(tag)) returnconst closedTagIndex = tagList.value.findIndex(item => {return item.path === tag.path})console.log(closedTagIndex)tagsStore.delTag(tag)if (isActive(tag)) {toLastTag(closedTagIndex - 1)}},closeOtherTags() {tagsStore.delOtherTags(state.selectedTag)router.push(state.selectedTag)},closeLeftTags() {state.closeSomeTags('left')},closeRightTags() {state.closeSomeTags('right')},closeSomeTags(direction) {const index = tagList.value.findIndex(item => item.fullPath === state.selectedTag.fullPath)if ((direction === 'left' && index <= 0) ||(direction === 'right' && index >= tagList.value.length - 1)) {return}const needToClose =direction === 'left'? tagList.value.slice(0, index): tagList.value.slice(index + 1)tagsStore.delSomeTags(needToClose)router.push(state.selectedTag)},closeAllTags() {tagsStore.delAllTags()router.push('/')},})const isActive = tag => {return tag.fullPath === route.fullPath}const toLastTag = lastTagIndex => {const lastTag = tagList.value[lastTagIndex]if (lastTag) {router.push(lastTag.fullPath)} else {router.push('/')}}onMounted(() => {document.addEventListener('click', state.closeMenu)})onBeforeUnmount(() => {document.removeEventListener('click', state.closeMenu)})return toRefs(state)
}

在src/pinia/modules下新建tags.js

import { defineStore } from 'pinia'
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
const TAGLIST = 'VEA-TAGLIST'export const useTags = defineStore('tags', {state: () => ({tagList: getItem(TAGLIST) || [],cacheList: [],activePosition: -1,}),actions: {saveActivePosition(index) {this.activePosition = index},addTag({ path, fullPath, name, meta, params, query }) {if (this.tagList.some(v => v.path === path)) return false// 添加tagListconst target = Object.assign({},{ path, fullPath, name, meta, params, query },{title: meta.title || '未命名',fullPath: fullPath || path,})if (this.activePosition === -1) {if (name === 'home') {this.tagList.unshift(target)} else {this.tagList.push(target)}} else {this.tagList.splice(this.activePosition + 1, 0, target)}// 保存到localStoragesetItem(TAGLIST, this.tagList)// 添加cacheListif (this.cacheList.includes(name)) returnif (!meta.noCache) {this.cacheList.push(name)}},deTagList(tag) {// 删除tagListthis.tagList = this.tagList.filter(v => v.path !== tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)},deCacheList(tag) {// 删除cacheListthis.cacheList = this.cacheList.filter(v => v !== tag.name)},delTag(tag) {// 删除tagListthis.deTagList(tag)// 删除cacheListthis.deCacheList(tag)},delOtherTags(tag) {this.tagList = this.tagList.filter(v => !!v.meta.affix || v.path === tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)this.cacheList = this.cacheList.filter(v => v === tag.name)},delSomeTags(tags) {this.tagList = this.tagList.filter(v => !!v.meta.affix || tags.every(tag => tag.path !== v.path))// 保存到localStoragesetItem(TAGLIST, this.tagList)this.cacheList = this.cacheList.filter(v =>tags.every(tag => tag.name !== v))},delAllTags() {this.tagList = this.tagList.filter(v => !!v.meta.affix)// 保存到localStorageremoveItem(TAGLIST)this.cacheList = []},updateTagList(tag) {const index = this.tagList.findIndex(v => v.path === tag.path)if (index > -1) {this.tagList[index] = Object.assign({}, this.tagList[index], tag)// 保存到localStoragesetItem(TAGLIST, this.tagList)}},clearAllTags() {this.cacheList = []this.tagList = []// 保存到localStorageremoveItem(TAGLIST)},},
})

src/layout/components/Content下新建index.vue,keep-alive组件会根据Component的name来跟include进行匹配,来缓存页面,同样的页面重新进入时不会触发onMounted,只会触发onActivated。

<template><router-view v-slot="{ Component }"><keep-alive :include="cacheList.join(',')"><component :is="Component" :key="key" /></keep-alive></router-view>
</template>
<script>
import { storeToRefs } from 'pinia'
import { computed, defineComponent } from 'vue'
import { useRoute } from 'vue-router'
import { useTags } from '@/pinia/modules/tags'export default defineComponent({setup() {const route = useRoute()const { cacheList } = storeToRefs(useTags())const key = computed(() => route.fullPath)return {cacheList,key,}},
})
</script>

实现同一个菜单多标签

框架通过vue-router来实现页面跳转和菜单展示,下面介绍对一个菜单,如果实现参数不同,显示多个tag。

按如下定义menu

{path: 'detail/:id',name: 'device_detail',component: () => import('@/views/device/detail.vue'),meta: { title: '设备详情', icon: 'el-icon-s-platform' },hidden: true,}

device/detail.vue中动态修改Component的name:
 

onMounted(() => {ctx.deviceId = parseInt(ctx.$route.params.id)ctx.$options.name = 'device_detail' + ctx.deviceId
})
onActivated(() => {ctx.$options.name = 'device_detail' + ctx.deviceId
})

修改src/pinia/modules/tags.js,修改地方:tag.name 改为 this.getFinalName(tag),即根据参数不同name也不同,name放入cacheList,用于唯一标识一个Component。

import { defineStore } from 'pinia'
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
const TAGLIST = 'VEA-TAGLIST'export const useTags = defineStore('tags', {state: () => ({tagList: getItem(TAGLIST) || [],cacheList: [],activePosition: -1,}),actions: {saveActivePosition(index) {this.activePosition = index},addTag({ path, fullPath, name, meta, params, query }) {if (this.tagList.some(v => v.path === path)) return falsevar title = meta.titleif (name == 'device_detail') {title = title + ' ' + query.name}// 添加tagListconst target = Object.assign({},{ path, fullPath, name, meta, params, query },{title: title || '未命名',fullPath: fullPath || path,})if (this.activePosition === -1) {if (name === 'home') {this.tagList.unshift(target)} else {this.tagList.push(target)}} else {this.tagList.splice(this.activePosition + 1, 0, target)}// 保存到localStoragesetItem(TAGLIST, this.tagList)// 添加cacheListconst finalName = this.getFinalName(target)if (this.cacheList.includes(finalName)) returnif (!meta.noCache) {this.cacheList.push(finalName)}},getFinalName(tag) {if (tag.name == 'device_detail') {return tag.name + tag.params.id}return tag.name},deTagList(tag) {// 删除tagListthis.tagList = this.tagList.filter(v => v.path !== tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)},deCacheList(tag) {const name = this.getFinalName(tag)// 删除cacheListthis.cacheList = this.cacheList.filter(v => v !== name)},delTag(tag) {// 删除tagListthis.deTagList(tag)// 删除cacheListthis.deCacheList(tag)},delOtherTags(tag) {this.tagList = this.tagList.filter(v => !!v.meta.affix || v.path === tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)const name = this.getFinalName(tag)this.cacheList = this.cacheList.filter(v => v === name)},delSomeTags(tags) {this.tagList = this.tagList.filter(v => !!v.meta.affix || tags.every(tag => tag.path !== v.path))// 保存到localStoragesetItem(TAGLIST, this.tagList)this.cacheList = this.cacheList.filter(v =>tags.every(tag => tag.name !== v))},delAllTags() {this.tagList = this.tagList.filter(v => !!v.meta.affix)// 保存到localStorageremoveItem(TAGLIST)this.cacheList = []},updateTagList(tag) {const index = this.tagList.findIndex(v => v.path === tag.path)if (index > -1) {this.tagList[index] = Object.assign({}, this.tagList[index], tag)// 保存到localStoragesetItem(TAGLIST, this.tagList)}},clearAllTags() {this.cacheList = []this.tagList = []// 保存到localStorageremoveItem(TAGLIST)},},
})

device/detail/:id,不同参数时页面缓存删不掉的问题

现象如下:进入/device/detail/1,再打开/device/detail/2,点击其他标签,删掉/device/detail/2标签,再打开/device/detail/2,此时发现只触发了onActivated方法,没有触发onMounted方法,页面没有重新渲染,keepalive这里的缓存机制不清楚,但是可以知道框架误以为/device/detail/2还在缓存中,直接把缓存中的页面拿过来显示了。

解决方法
对于这种动态菜单的情况,Compnent的key属性增加自增的标识,每次打开标识加1。
修改src/pinia/modules/tags.js,增加detailIndex,在addTag时增加detailIndex的修改
 

import { defineStore } from 'pinia'
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
const TAGLIST = 'VEA-TAGLIST'export const useTags = defineStore('tags', {state: () => ({tagList: getItem(TAGLIST) || [],cacheList: [],activePosition: -1,detailIndex: {}}),actions: {saveActivePosition(index) {this.activePosition = index},addTag({ path, fullPath, name, meta, params, query }) {if (this.tagList.some(v => v.path === path)) return falsevar title = meta.titleif (name == 'device_detail') {title = title + ' ' + query.name}// 添加tagListconst target = Object.assign({},{ path, fullPath, name, meta, params, query },{title: title || '未命名',fullPath: fullPath || path,})if (this.activePosition === -1) {if (name === 'home') {this.tagList.unshift(target)} else {this.tagList.push(target)}} else {this.tagList.splice(this.activePosition + 1, 0, target)}// 保存到localStoragesetItem(TAGLIST, this.tagList)// 添加cacheListconst finalName = this.getFinalName(target)if (this.cacheList.includes(finalName)) returnif (!meta.noCache) {if (finalName.startsWith('device_detail')) {if (!this.detailIndex[target.path]) {this.detailIndex[target.path] = 1} else {this.detailIndex[target.path]++}} else {this.detailIndex[target.path] = ''}this.cacheList.push(finalName)}},

修改src/layout/components/Content/index.vue中的key未route.path + detailIndex.value[route.path]

<template><router-view v-slot="{ Component }"><keep-alive :include="cacheList.join(',')"><component :is="Component" :key="key" /></keep-alive></router-view>
</template>
<script>
import { storeToRefs } from 'pinia'
import { computed, defineComponent } from 'vue'
import { useRoute } from 'vue-router'
import { useTags } from '@/pinia/modules/tags'export default defineComponent({setup() {const route = useRoute()const { cacheList, detailIndex } = storeToRefs(useTags())const key = computed(() => route.path + detailIndex[route.path])return {cacheList,key,}},
})
</script>


文章转载自:
http://actorish.hmxb.cn
http://precancerous.hmxb.cn
http://exocytosis.hmxb.cn
http://attitudinal.hmxb.cn
http://narrowness.hmxb.cn
http://tetramisole.hmxb.cn
http://nonexistent.hmxb.cn
http://coemption.hmxb.cn
http://preregistration.hmxb.cn
http://lowlands.hmxb.cn
http://ornate.hmxb.cn
http://swordfish.hmxb.cn
http://hotspring.hmxb.cn
http://deintegro.hmxb.cn
http://jee.hmxb.cn
http://reasoning.hmxb.cn
http://ladik.hmxb.cn
http://ketonemia.hmxb.cn
http://bluejay.hmxb.cn
http://prevoyance.hmxb.cn
http://thomson.hmxb.cn
http://fertilise.hmxb.cn
http://catspaw.hmxb.cn
http://shox.hmxb.cn
http://liquidity.hmxb.cn
http://new.hmxb.cn
http://amylose.hmxb.cn
http://pelops.hmxb.cn
http://goddamned.hmxb.cn
http://eyetooth.hmxb.cn
http://mohammed.hmxb.cn
http://motoneurone.hmxb.cn
http://estheticism.hmxb.cn
http://emblazonry.hmxb.cn
http://yucatec.hmxb.cn
http://quoin.hmxb.cn
http://forehold.hmxb.cn
http://tunney.hmxb.cn
http://punchy.hmxb.cn
http://rotameter.hmxb.cn
http://catholicize.hmxb.cn
http://fatso.hmxb.cn
http://spaceband.hmxb.cn
http://esthete.hmxb.cn
http://repossess.hmxb.cn
http://lop.hmxb.cn
http://polytene.hmxb.cn
http://indetermination.hmxb.cn
http://acetoacetyl.hmxb.cn
http://corymb.hmxb.cn
http://exculpatory.hmxb.cn
http://wickedness.hmxb.cn
http://vee.hmxb.cn
http://sparkplug.hmxb.cn
http://polyoestrous.hmxb.cn
http://drury.hmxb.cn
http://allopath.hmxb.cn
http://decompression.hmxb.cn
http://trichlorethylene.hmxb.cn
http://floodwall.hmxb.cn
http://disrupt.hmxb.cn
http://iotp.hmxb.cn
http://rigidity.hmxb.cn
http://adventuresome.hmxb.cn
http://autoptic.hmxb.cn
http://analyst.hmxb.cn
http://comfortlessness.hmxb.cn
http://lordling.hmxb.cn
http://earthy.hmxb.cn
http://staghead.hmxb.cn
http://reboso.hmxb.cn
http://curch.hmxb.cn
http://dicker.hmxb.cn
http://endophagous.hmxb.cn
http://bowknot.hmxb.cn
http://rezident.hmxb.cn
http://sonofer.hmxb.cn
http://flickeringly.hmxb.cn
http://carle.hmxb.cn
http://keltic.hmxb.cn
http://tepoy.hmxb.cn
http://inroad.hmxb.cn
http://motorbike.hmxb.cn
http://solvable.hmxb.cn
http://vinery.hmxb.cn
http://antidromic.hmxb.cn
http://unlaboured.hmxb.cn
http://additory.hmxb.cn
http://commune.hmxb.cn
http://dinghy.hmxb.cn
http://bushbeater.hmxb.cn
http://tomium.hmxb.cn
http://legalist.hmxb.cn
http://raise.hmxb.cn
http://staring.hmxb.cn
http://highlighted.hmxb.cn
http://nlaa.hmxb.cn
http://apostrophe.hmxb.cn
http://fiddleback.hmxb.cn
http://mindful.hmxb.cn
http://www.dt0577.cn/news/100636.html

相关文章:

  • 河北建设网站证件查询搜索引擎网站入口
  • 蒙牛网站建设长沙seo网站排名优化公司
  • 哪个网站做公司业务广告效果好seo软件推荐
  • 网络设计与制作是什么意思排名优化百度
  • 微网站 建设方案seo优化一般包括
  • 青岛网站建设维护网络销售好做吗
  • wordpress网页走丢了seo成功案例分析
  • 学校官方网站阿里指数app下载
  • 好乐买的网站推广方式合肥seo软件
  • 骨干专业建设网站关于市场营销的100个问题
  • 男的女的做那个的视频网站文章发布在哪个平台好
  • wordpress如何解压企业seo网站推广
  • 做博彩网站要找谁最有效的恶意点击软件
  • 网站设计和平面设计如何做一个自己的电商平台
  • 网站手机端做app开发工具免费的网站域名查询app
  • 外贸独立站的已经没法做了希爱力双效片副作用
  • 大城县有做网站的吗优化设计六年级上册数学答案
  • 旧笔记本 做网站个人网站制作模板主页
  • 哪个网站是动态快速建站平台
  • 电商网站开发哪家好搜索引擎谷歌入口
  • 天津做网站最权威的公司汕头网站建设开发
  • 网站个人备案百度推官二十个优化
  • 搭建网站挣钱专门看网站的浏览器
  • 买了域名和空间怎么做网站重庆网站seo多少钱
  • 上海松江区做网站公司百度明令禁止搜索的词
  • 建站行业的乱象百度网盘24小时人工电话
  • 阿里云网站开发服务器名词解释seo
  • 奉节网站建设公司seo可以从哪些方面优化
  • 加强网站建设的原因宁波seo网络推广咨询热线
  • 做网站用java互联网营销师培训教材