Чуть больше кода для этой статьи, надеюсь вы терпеливо посмотрите
Введение
В 2020 годуVue3ИзучениеVue3небольшие проектыnav-url, а также мои советы о том, как быстро приступить к работеVue3Несколько блогов, я очень рад, что мне все указали и понравились:
В последнем блоге я подробно представил функции, основные моменты и реализацию всех основных функций первой версии проекта, который я выпустил. Надеюсь, вы сможете прочитать и испытать его (не забудьте открыть его на компьютере, потому что это проект для ПК)
Тем не менее, этот проект просто реализует некоторые функции, но я не чувствую, что он используется очень хорошо.Composition APIИнтегрировать управление кодом. Знать,Composition APIказалось, решитьOptions APIВызывает явление, что один и тот же код функции разбросан, и есть много больших парней, которые сделали много анимационных отображений на нем (я позаимствовал его здесьДашюи занимается полным стекомАнимацию сделал шеф, его статью можно сказать хвалят снова и снова, можете посмотреть:Я сделал ночь анимации, просто чтобы все лучше поняли Composition API Vue3.)
Я посмотрел на код первой версии своего проекта, и он просто не отразилсяComposition APIПреимущество, вы можете показать вам код в компоненте
<template>
<aside id="tabs-container">
<div id="logo-container">
{{ navInfos.navName }}
</div>
<ul id="tabs">
<li class="tab tab-search" @click="showSearch">
<i class="fas fa-search tab-icon"/>
<span>快速搜索</span>
</li>
<li class="tab tab-save" @click="showSaveConfigAlert">
<i class="fas fa-share-square tab-icon"></i>
<span>保存配置</span>
</li>
<li class="tab tab-import" @click="showImportConfigAlert">
<i class="fas fa-cog tab-icon"></i>
<span>导入配置</span>
</li>
<br>
<li v-for="(item, index) in navInfos.catalogue"
:key="index"
class="tab"
@click="toID(item.id)">
<span class="li-container">
<i :class="['fas', `fa-${item.icon}`, 'tab-icon']" />
<span>{{ item.name }}</span>
<i class="fas fa-angle-right tab-icon tab-angle-right"/>
</span>
</li>
<li class="tab add-tab" @click="addTabShow">
<i class="fas fa-plus"/>
</li>
</ul>
<!-- 添加标签弹框 -->
<tabAlert />
<!-- 保存配置弹框 -->
<save-config @closeSaveConfigAlert="closeSaveConfigAlert" :isShow="isShowSaveAlert"/>
<!-- 导入配置弹框 -->
<import-config @closeImportConfigAlert="closeImportConfigAlert" :isShow="isShowImportAlert"/>
</aside>
</template>
<script>
import {ref} from 'vue'
import {useStore} from 'vuex'
import tabAlert from '../public/tabAlert/tabAlert'
import saveConfig from './childCpn/saveConfig'
import importConfig from './childCpn/importConfig'
export default {
name: 'tabs',
components: {
tabAlert,
saveConfig,
importConfig
},
setup() {
const store = useStore()
let navInfos = store.state // Vuex的state对象
let isShowSaveAlert = ref(false) // 保存配置弹框是否展示
let isShowImportAlert = ref(false) // 导入配置弹框是否展示
// 展示"添加标签弹框"
function addTabShow() {
store.commit('changeTabInfo', [
{key: 'isShowAddTabAlert', value: true},
{key: 'alertType', value: '新增标签'}
])
}
// 关闭"保存配置弹框"
function closeSaveConfigAlert(value) {
isShowSaveAlert.value = value
}
// 展示"保存配置弹框"
function showSaveConfigAlert() {
isShowSaveAlert.value = true
}
// 展示"导入配置弹框"
function showImportConfigAlert() {
isShowImportAlert.value = true
}
// 关闭"导入配置弹框"
function closeImportConfigAlert(value) {
isShowImportAlert.value = value
}
// 展示搜索框
function showSearch() {
if(store.state.moduleSearch.isSearch) {
store.commit('changeIsSearch', false)
store.commit('changeSearchWord', '')
} else {
store.commit('changeIsSearch', true)
}
}
// 跳转到指定标签
function toID(id) {
const content = document.getElementById('content')
const el = document.getElementById(`${id}`)
let start = content.scrollTop
let end = el.offsetTop - 80
let each = start > end ? -1 * Math.abs(start - end) / 20 : Math.abs(start - end) / 20
let count = 0
let timer = setInterval(() => {
if(count < 20) {
content.scrollTop += each
count ++
} else {
clearInterval(timer)
}
}, 10)
}
return {
navInfos,
addTabShow,
isShowSaveAlert,
closeSaveConfigAlert,
showSaveConfigAlert,
isShowImportAlert,
showImportConfigAlert,
closeImportConfigAlert,
showSearch,
toID
}
}
}
</script>
Приведенный выше код находится в моем проектеБоковая панельВсе переменные и методы, хотя существуют переменные и методы вsetupФункция есть, но все еще выглядит неорганизованной.Если бизнес-требования этого компонента становятся все более и более сложными, этотsetupКод внутри может быть грязнее
Итак, я начал думать о том, как абстрагироваться от своего кода. Позже я рассказал о своих мыслях о температуре кипения Наггетс и попросил других копателей поделиться своими соображениями.
На самом деле, ответ последнего брата меня очень вдохновил, поэтому я также позаимствовал его идеи для извлечения кода моего проекта.
Готов к работе
Прежде всего, я должен подумать над вопросом: при извлечении кода он извлекается отдельно по компонентам? Или он извлекается в соответствии с общей функцией?
Наконец, я решил извлечь код в соответствии с общей функцией. Конкретный список функций выглядит следующим образом:
- Функция поиска
- Добавить/изменить функцию метки
- Добавить/изменить функцию URL
- Функция импорта конфигурации
- Функция экспорта конфигурации
- Функция редактирования
Начать извлечение кода
Каждая из вышеперечисленных функций будет передаватьJSфайл для хранения переменных и методов, соответствующих функции. тогда всеJSфайлы помещаются вsrc/useвниз, как показано на рисунке
просто возьмиДобавить/изменить функцию меткиНапример, используйте движущееся изображение, чтобы показать вам все эффекты этой функции.
Очевидно, я сделал компонент всплывающего окна, когда я нажимаю на боковую панель+После ввода номера отображается всплывающее окно; затем я ввожу имя метки, которую хочу добавить, выбираю соответствующий значок и, наконец, нажимаю «ОК», так что метка добавляется, и всплывающее окно также скрытый;
Наконец, я перешел в режим редактирования и щелкнул, чтобы изменить метку, снова отобразилось всплывающее окно, и в то же время отобразились имя и значок соответствующей метки; после того, как я изменил имя, я щелкнул, чтобы подтвердите, значит информация ярлыка была изменена мной.Ну всплывающее окно снова скрыто.
Таким образом, следующие функции резюмируются следующим образом:
- Всплывающий дисплей
- Скрытие всплывающего окна
- Нажмите, чтобы подтвердить и добавить или изменить содержимое ярлыка.
По традиционному способу написания реализация трех вышеперечисленных функций выглядит так (я модифицировал и упростил код, смысл всем понятен):
- Содержимое компонента боковой панели
<!-- 侧边栏组件内容 -->
<template>
<aside>
<div @click="show">新增标签</div>
<tab-alert :isShow="isShow" @closeTabAlert="close"/>
</aside>
</template>
<script>
import { ref } from 'vue'
import tabAlert from '@/components/tabAlert/index'
export default {
name: "tab",
components: {
tabAlert
},
setup() {
// 存储标签弹框的展示情况
const isShow = ref(false)
// 展示标签弹框
function show() {
isShow.value = true
}
// 隐藏标签弹框
function close() {
isShow.value = false
}
return { isShow, show, close }
}
}
</script>
- Содержимое всплывающего компонента ярлыка
<!-- 标签弹框组件内容 -->
<template>
<div v-show="isShow">
<!-- 此处省略一部分不重要的内容代码 -->
<div @click="close">取消</div>
<div @click="confirm">确认</div>
</div>
</template>
<script>
export default {
name: "tab",
props: {
isShow: {
type: Boolean,
default: false
}
},
setup(props, {emit}) {
// 隐藏标签弹框
function close() {
emit('close')
}
// 点击确认后的操作
function confirm() {
/* 此处省略点击确认按钮后更新标签内容的业务代码 */
close()
}
return { close, confirm }
}
}
</script>
После прочтения приведенного выше кода вы можете обнаружить, что реализация простой функции включает в себя два компонента, а также требует, чтобы родительский и дочерний компоненты взаимодействовали друг с другом для управления некоторыми состояниями, чтобы функция не разбивалась, т.е. Недостаточно агрегации. Поэтому при извлечении этих функциональных кодов по функциям я создамtabAlert.jsфайл, в котором хранятся все переменные и методы этой функции.
tabAlert.jsОбщая структура файла такова:
// 引入依赖API
import { ref } from 'vue'
// 定义一些变量
const isShow = ref(false) // 存储标签弹框的展示状态
export default function tabAlertFunction() {
/* 定义一些方法 */
// 展示标签弹框
function show() {
isShow.value = true
}
// 关闭标签弹框
function close() {
isShow.value = false
}
// 点击确认按钮以后的操作
function confirm() {
/* 此处省略点击确认按钮后更新标签内容的业务代码 */
close()
}
return {
isShow,
show,
close,
confirm,
}
}
Что касается того, почему такая структура разработана, давайте начнем с экспортируемого метода.Я помещаю все методы, связанные с этой функцией, в функцию и, наконец, передаюreturnЭкспортируйте, потому что иногда эти методы зависят от других внешних переменных, поэтому оберните слой функциями, например:
// example.js
export default function exampleFunction(num) {
function log1() {
console.log(num + 1)
}
function log2() {
console.log(num + 2)
}
return {
log1,
log2,
}
}
Из этого файла мы находим, чтоlog1а такжеlog2методы зависят от переменныхnum, но мы не определили переменную в этом файлеnum, то когда файл вводится в другие компоненты, его можно отдать самому внешнемуexampleFunctionметод передачи параметраnumТолько что
<template>
<button @click="log1">打印加1</button>
<button @click="log2">打印加2</button>
</template>
<script>
import exampleFunction from './example'
import { num } from './getNum' // 假设num是从别的模块中获取到的
export default {
setup() {
let { log1, log2 } = exampleFunction(num)
return { log1, log2 }
}
}
</script>
Тогда давайте поговорим о том, почему определение переменной находится за пределами нашей экспортируемой функции. Продолжайте смотреть на пример функции вкладки в моем проекте, который я привел выше, переменная, используемая для хранения состояния отображения всплывающего окна вкладки.isShowОна определена в компоненте, и компонент метки также должен получить эту переменную для управления состоянием дисплея.Используется связь между родительским и дочерним компонентами, поэтому мы могли бы также записать эту переменную в общедоступный файл, независимо от того, какой компонент нужен. Когда вы его используете, вам нужно только импортировать и получить его, потому что каждый раз вы получаете одну и ту же переменную
Таким образом, не будет ли сохранено даже взаимодействие между родительскими и дочерними компонентами?
Мы только что упаковалиtabAlert.jsИспользуйте его в компоненте, чтобы увидеть, каков эффект
- Содержимое компонента боковой панели
<!-- 侧边栏组件内容 -->
<template>
<aside>
<div @click="show">新增标签</div>
<tab-alert/>
</aside>
</template>
<script>
import tabAlert from '@/components/tabAlert/index'
import tabAlertFunction from '@/use/tabAlert'
export default {
name: "tab",
components: {
tabAlert
},
setup() {
let { show } = tabAlertFunction()
return { show }
}
}
</script>
- Содержимое всплывающего компонента ярлыка
<!-- 标签弹框组件内容 -->
<template>
<div v-show="isShow">
<!-- 此处省略一部分不重要的内容代码 -->
<div @click="close">取消</div>
<div @click="confirm">确认</div>
</div>
</template>
<script>
import tabAlertFunction from '@/use/tabAlert'
export default {
name: "tab",
setup() {
let { isShow, close, confirm } = tabAlertFunction()
return { isShow, close, confirm }
}
}
</script>
В это время переверните его и посмотрите на исходный код, чувствуете ли вы, что после того, как код извлечен, он становится нестандартным, и кода в компоненте намного меньше.
Таким образом, метод сбора переменных и кодов через функции, я лично считаю, что им легче управлять.Если вы хотите добавить какие-либо небольшие требования к этой функции однажды в будущем, просто найдитеtabAlert.jsВ этом файле вы можете писать методы и переменные.
Показать ссылку
Я просто использовал этот метод, чтобы извлечь исходный код. Вот несколько групп, которые вы можете увидеть.Отсоединен назада такжеПосле отстранениясравнение кода
Контраст один
- Прежде чем оторваться
<template>
<div class="import-config-container" v-show="isShow">
<div class="import-config-alert">
<div class="close-import-config-alert" @click="closeAlert"></div>
<div class="import-config-alert-title">导入配置</div>
<div class="import-config-alert-remind">说明:需要上传之前保存导出的xxx.json配置文件,文件中的信息会完全覆盖当前信息</div>
<form action="" class="form">
<label for="import_config_input" class="import-config-label">
上传配置文件
<i v-if="hasFile == 1" class="fas fa-times-circle uploadErr uploadIcon"/>
<i v-else-if="hasFile == 2" class="fas fa-check-circle uploadSuccess uploadIcon"/>
</label>
<input id="import_config_input" type="file" class="select-file" ref="inputFile" @change="fileChange">
</form>
<lp-button type="primary" class="import-config-btn" @_click="importConfig">确认上传</lp-button>
</div>
</div>
</template>
<script>
import {ref, inject} from 'vue'
import lpButton from '../../public/lp-button/lp-button'
export default {
props: {
isShow: {
type: Boolean,
default: true
}
},
components: {
lpButton
},
setup(props, {emit}) {
const result = ref('none') // 导入的结果
const isUpload = ref(false) // 判断是否上传配置文件
const isImport = ref(false) // 判断配置是否导入成功
const isLoading = ref(false) // 判断按钮是否处于加载状态
const inputFile = ref(null) // 获取文件标签
const hasFile = ref(0) // 判断文件的传入情况。0:未传入 1: 格式错误 2:格式正确
const $message = inject('message')
// 导入配置
function importConfig() {
let reader = new FileReader()
let files = inputFile.value.files
if(hasFile.value == 0) {
$message({
type: 'warning',
content: '请先上传配置文件'
})
}
else if(hasFile.value == 1) {
$message({
type: 'warning',
content: '请上传正确格式的文件,例如xx.json'
})
}
else if(hasFile.value == 2) {
reader.readAsText(files[0])
reader.onload = function() {
let data = this.result
window.localStorage.navInfos = data
location.reload()
}
}
}
// 关闭弹窗
function closeAlert() {
emit('closeImportConfigAlert', false)
hasFile.value = 0
}
function fileChange(e) {
let files = e.target.files
if(files.length === 0) {
$message({
type: 'warning',
content: '请先上传配置文件'
})
}
else {
let targetFile = files[0]
if(!/\.json$/.test(targetFile.name)) {
hasFile.value = 1
$message({
type: 'warning',
content: '请确认文件格式是否正确'
})
} else {
hasFile.value = 2
$message({
type: 'success',
content: '文件格式正确'
})
}
}
}
return {
result,
isUpload,
isImport,
isLoading,
importConfig,
closeAlert,
inputFile,
fileChange,
hasFile
}
}
}
</script>
- После отстранения
<template>
<div class="import-config-container" v-show="isShowImportAlert">
<div class="import-config-alert">
<div class="close-import-config-alert" @click="handleImportConfigAlert(false)"></div>
<div class="import-config-alert-title">导入配置</div>
<div class="import-config-alert-remind">说明:需要上传之前保存导出的xxx.json配置文件,文件中的信息会完全覆盖当前信息</div>
<form action="" class="form">
<label for="import_config_input" class="import-config-label">
上传配置文件
<i v-if="hasFile == 1" class="fas fa-times-circle uploadErr uploadIcon"/>
<i v-else-if="hasFile == 2" class="fas fa-check-circle uploadSuccess uploadIcon"/>
</label>
<input id="import_config_input" type="file" class="select-file" ref="inputFile" @change="fileChange">
</form>
<lp-button type="primary" class="import-config-btn" @_click="importConfig">确认上传</lp-button>
</div>
</div>
</template>
<script>
/* API */
import { inject } from 'vue'
/* 组件 */
import lpButton from '@/components/public/lp-button/lp-button'
/* 功能模块 */
import importConfigFunction from '@/use/importConfig'
export default {
components: {
lpButton
},
setup() {
const $message = inject('message')
const {
isShowImportAlert,
handleImportConfigAlert,
result,
isUpload,
isImport,
isLoading,
importConfig,
closeAlert,
inputFile,
fileChange,
hasFile
} = importConfigFunction($message)
return {
isShowImportAlert,
handleImportConfigAlert,
result,
isUpload,
isImport,
isLoading,
importConfig,
closeAlert,
inputFile,
fileChange,
hasFile
}
}
}
</script>
- Извлеченный файл кода
// 导入配置功能
import { ref } from 'vue'
const isShowImportAlert = ref(false), // 导入配置弹框是否展示
result = ref('none'), // 导入的结果
isUpload = ref(false), // 判断是否上传配置文件
isImport = ref(false), // 判断配置是否导入成功
isLoading = ref(false), // 判断按钮是否处于加载状态
inputFile = ref(null), // 获取文件元素
hasFile = ref(0) // 判断文件的传入情况。0:未传入 1: 格式错误 2:格式正确
export default function importConfigFunction($message) {
// 控制弹框的展示
function handleImportConfigAlert(value) {
isShowImportAlert.value = value
if(!value) hasFile.value = 0
}
// 上传的文件内容发生改变
function fileChange(e) {
let files = e.target.files
if(files.length === 0) {
$message({
type: 'warning',
content: '请先上传配置文件'
})
}
else {
let targetFile = files[0]
if(!/\.json$/.test(targetFile.name)) {
hasFile.value = 1
$message({
type: 'warning',
content: '请确认文件格式是否正确'
})
} else {
hasFile.value = 2
$message({
type: 'success',
content: '文件格式正确'
})
}
}
}
// 导入配置
function importConfig() {
let reader = new FileReader()
let files = inputFile.value.files
if(hasFile.value == 0) {
$message({
type: 'warning',
content: '请先上传配置文件'
})
}
else if(hasFile.value == 1) {
$message({
type: 'warning',
content: '请上传正确格式的文件,例如xx.json'
})
}
else if(hasFile.value == 2) {
reader.readAsText(files[0])
reader.onload = function() {
let data = this.result
window.localStorage.navInfos = data
location.reload()
}
}
}
return {
isShowImportAlert,
result,
isUpload,
isImport,
isLoading,
inputFile,
hasFile,
handleImportConfigAlert,
fileChange,
importConfig,
}
}
Контраст два
- Прежде чем оторваться
<template>
<!-- 此处因代码太多,暂时省略,详情可以点击文末的项目源码查看 -->
</template>
<script>
import {ref, inject} from 'vue'
import {useStore} from 'vuex'
import urlAlert from '../public/urlAlert/urlAlert'
import tagAlert from '../public/tabAlert/tabAlert'
import carousel from './childCpn/carousel'
import search from './childCpn/search'
import { exchangeElements } from '../../utils/utils'
export default {
components: {
urlAlert,
tagAlert,
carousel,
search,
},
setup() {
const store = useStore()
const catalogue = store.state.catalogue
const moduleUrl = store.state.moduleUrl
const moduleSearch = store.state.moduleSearch
const $message = inject('message')
const $confirm = inject('confirm')
const editWhich = ref(-1)
// 弹出添加URL的框
function addMoreUrl(id) {
store.commit('changeUrlInfo', [
{key: 'isShow', value: true},
{key: 'whichTag', value: id},
{key: 'alertType', value: '新增网址'}
])
}
// 处理无icon或icon加载失败的图片,令其使用默认svg图标
function imgLoadErr(e) {
let el = e.target
el.style.display = 'none'
el.nextSibling.style.display = 'inline-block'
}
function imgLoadSuccess(e) {
let el = e.target
el.style.display = 'inline-block'
el.nextSibling.style.display = 'none'
}
// 进入编辑状态
function enterEdit(id) {
if(id != editWhich.value) {
editWhich.value = id
} else {
editWhich.value = -1
}
}
// 修改标签弹框弹出
function editTagAlertShow(tab) {
store.commit('changeTabInfo', [
{key: 'isShowAddTabAlert', value: true},
{key: 'tagName', value: tab.name},
{key: 'trueIcon', value: tab.icon},
{key: 'isSelected', value: true},
{key: 'currentIcon', value: tab.icon},
{key: 'id', value: tab.id},
{key: 'alertType', value: '修改标签'}
])
}
// 删除标签以及标签下的所有网址
function deleteTag(id) {
$confirm({
content: '确定删除该标签以及该标签下所有网址吗?'
})
.then(() => {
store.commit('remove', id)
$message({
type: 'success',
content: '标签页及子网址删除成功'
})
})
.catch(() => {})
}
// 删除某个网址
function deleteUrl(id) {
$confirm({
content: '确定删除该网址吗?'
})
.then(() => {
store.commit('remove', id)
$message({
type: 'success',
content: '网址删除成功'
})
})
.catch(() => {})
}
// 弹出修改URL的弹框
function editUrl(url) {
store.commit('changeUrlInfo', [
{key: 'url', value: url.url},
{key: 'icon', value: url.icon},
{key: 'id', value: url.id},
{key: 'name', value: url.name},
{key: 'isShow', value: true},
{key: 'alertType', value: '修改网址'}
])
}
function judgeTabIsShow(i) {
const URLS = catalogue[i]['URLS']
let length = URLS.length
for(let j = 0; j < length; j++) {
if(moduleSearch.searchWord == '') return false;
else if(URLS[j].name.toLowerCase().indexOf(moduleSearch.searchWord.toLowerCase()) !== -1) return true;
}
return false
}
function judgeUrlIsShow(i, j) {
const url = catalogue[i]['URLS'][j]
if(url.name.toLowerCase().indexOf(moduleSearch.searchWord.toLowerCase()) !== -1) return true;
return false;
}
let elementNodeDragged = null // 被移动的地址框元素
let elementNodeLocated = null // 移入的地址框元素
let draggedId = -1 // 被移动地址框的id
// 地址框开始拖拽
function urlBoxDragStart(e) {
const el = e.target
if(el.nodeName !== 'LI') return;
// 记录当前被拖拽地址框元素
elementNodeDragged = el
// 将被拖拽对象隐藏
el.style.display = 'fixed'
el.style.opacity = 0
}
// 地址框拖拽结束
function urlBoxDragEnd(e) {
let el = e.target
el.style.display = 'inline-block'
el.style.opacity = 1
// 获取当前正在编辑标签中所有url的排序
let timer = setTimeout(() => {
const result = []
const children = elementNodeLocated.parentNode.children
let length = children.length
for(let i = 0; i < length - 1; i++) {
result.push(children[i].getAttribute('data-id'))
}
store.commit('dragEndToUpdate', {tabId: editWhich.value, result})
clearTimeout(timer)
}, 500)
}
// 被拖动的地址框触碰到其它的地址框
function urlBoxEnter(e, tabId) {
if(tabId != editWhich.value) return;
let el = e.target
while(el.nodeName !== 'LI') el = el.parentNode; // 若子元素触发dragenter事件,则查找到父元素li标签
if(el === elementNodeDragged) return; // 避免自己拖拽进入自己的情况
if(elementNodeLocated !== el) elementNodeLocated = el // 记录被移入的地址框元素
exchangeElements(elementNodeDragged, el) // 地址框位置互换
}
return {
catalogue,
addMoreUrl,
moduleUrl,
moduleSearch,
imgLoadErr,
imgLoadSuccess,
enterEdit,
editTagAlertShow,
deleteTag,
deleteUrl,
editUrl,
editWhich,
judgeTabIsShow,
judgeUrlIsShow,
urlBoxDragStart,
urlBoxDragEnd,
urlBoxEnter,
}
}
}
</script>
- После отстранения
<template>
<!-- 此处因代码太多,暂时省略,详情可以点击文末的项目源码查看 -->
</template>
<script>
/* API */
import { inject } from 'vue'
import { useStore } from 'vuex'
/* 组件 */
import urlAlert from '@/components/public/urlAlert/index'
import tabAlert from '@/components/public/tabAlert/index'
import carousel from './carousel'
import search from './search'
/* 功能模块 */
import baseFunction from '@/use/base'
import editFunction from '@/use/edit'
import urlAlertFunction from '@/use/urlAlert'
import tabAlertFunction from '@/use/tabAlert'
import searchFunction from '@/use/search'
export default {
components: {
urlAlert,
tabAlert,
carousel,
search,
},
setup() {
const catalogue = useStore().state.catalogue
const $message = inject('message')
const $confirm = inject('confirm')
// 一些基础的方法
let { imgLoadErr, imgLoadSuccess } = baseFunction()
// url框编辑下的相关变量及功能
let {
editWhich,
handleEdit,
deleteTab,
deleteUrl,
urlBoxDragStart,
urlBoxDragEnd,
urlBoxEnter
} = editFunction($message, $confirm)
// 弹出 “新增”、“修改” url弹框
let { showNewUrlAlert, showEditUrlAlert } = urlAlertFunction()
// 搜索功能相关的变量及方法
let { moduleSearch, judgeTabIsShow, judgeUrlIsShow } = searchFunction()
// 展示修改tab的弹框
let { showEditAddTab } = tabAlertFunction()
return {
catalogue,
showNewUrlAlert,
moduleSearch,
imgLoadErr,
imgLoadSuccess,
handleEdit,
showEditAddTab,
deleteTab,
deleteUrl,
showEditUrlAlert,
editWhich,
judgeTabIsShow,
judgeUrlIsShow,
urlBoxDragStart,
urlBoxDragEnd,
urlBoxEnter,
}
}
}
</script>
- Извлеченные файлы кода (здесь задействовано много модулей, поэтому показаны только два)
// 搜索功能
import { } from 'vue'
import store from '@/store/index'
// 变量
const moduleSearch = store.state.moduleSearch // 搜索相关的全局状态
export default function searchFunction() {
// 搜索框的输入改变
function inputSearchContent(value) {
store.commit('changeSearchWord', value)
}
// 控制搜索框的展示
function handleSearchBox() {
if(moduleSearch.isSearch) {
store.commit('changeIsSearch', false)
store.commit('changeSearchWord', '')
} else {
store.commit('changeIsSearch', true)
}
}
// 判断标签是否显示
function judgeTabIsShow(i) {
return store.getters.judgeTabIsShow(i)
}
// 判断url是否显示
function judgeUrlIsShow(i, j) {
return store.getters.judgeUrlIsShow(i, j)
}
return {
moduleSearch,
inputSearchContent,
handleSearchBox,
judgeTabIsShow,
judgeUrlIsShow,
}
}
// url框的拖拽排列
import { ref } from 'vue'
import { exchangeElements, debounce } from '@/utils/utils'
import store from '@/store/index'
//变量
let elementNodeDragged = null, // 被移动的地址框元素
elementNodeLocated = null, // 移入的地址框元素
editWhich = ref(-1) // 记录正在编辑的tab索引
export default function editFunction($message, $confirm) {
// 控制编辑状态
function handleEdit(id) {
if(id != editWhich.value) {
editWhich.value = id
} else {
editWhich.value = -1
}
}
// 删除标签以及标签下的所有网址
function deleteTab(id) {
$confirm({
content: '确定删除该标签以及该标签下所有网址吗?'
})
.then(() => {
store.commit('remove', id)
$message({
type: 'success',
content: '标签页及子网址删除成功'
})
})
.catch(() => {})
}
// 删除某个网址
function deleteUrl(id) {
$confirm({
content: '确定删除该网址吗?'
})
.then(() => {
store.commit('remove', id)
$message({
type: 'success',
content: '网址删除成功'
})
})
.catch(() => {})
}
// 地址框开始拖拽
function urlBoxDragStart(e) {
const el = e.target
if(el.nodeName !== 'LI') return;
// 记录当前被拖拽地址框元素
elementNodeDragged = el
// 将被拖拽对象隐藏
el.style.display = 'fixed'
el.style.opacity = 0
}
// 拖拽后更新Vuex中的正确排序
let dragEndToUpdate = debounce(function() {
// 获取当前正在编辑标签中所有url的排序
const result = []
const children = elementNodeLocated.parentNode.children
let length = children.length
for(let i = 0; i < length - 1; i++) {
result.push(children[i].getAttribute('data-id'))
}
store.commit('dragEndToUpdate', {tabId: editWhich.value, result})
}, 500)
// 地址框拖拽结束
function urlBoxDragEnd(e) {
let el = e.target
el.style.display = 'inline-block'
el.style.opacity = 1
dragEndToUpdate()
}
// 被拖动的地址框触碰到其它的地址框
function urlBoxEnter(e, tabId) {
if(tabId != editWhich.value) return;
let el = e.target
while(el.nodeName !== 'LI') el = el.parentNode; // 若子元素触发dragenter事件,则查找到父元素li标签
if(el === elementNodeDragged) return; // 避免自己拖拽进入自己的情况
if(elementNodeLocated !== el) elementNodeLocated = el // 记录被移入的地址框元素
exchangeElements(elementNodeDragged, el) // 地址框位置互换
}
return {
editWhich,
handleEdit,
deleteTab,
deleteUrl,
urlBoxDragStart,
urlBoxDragEnd,
urlBoxEnter,
}
}
наконец
Внимательные друзья должны были обнаружить, что в коде, который я вам только что показал, есть секция различных методов реализации перетаскивания, это верно! ! Я добавил режим редактирования в свой проект в свободное времяФункция перетаскиванияСчитается, что это можно считать выполнить одну из рекомендаций, которые я упомянул вас перед вами, чтобы приветствовать вас всем, чтобы перейти к новым функциям -
После опыта, я надеюсь, что заинтересованные друзья смогутgithubдай мне упоминаниеIssues, я отвечу, как только увижу это (если есть много друзей, которые призывают меня использовать функцию учетной записи, я могу добавить ее позже)
Ссылка на источник проекта(Приветствуем всех звезд, высказывайте больше мнений и больше общайтесь~)
Код вытащил метод, описанный в этой статье, это перевернуть много раз я заложил, я не знаю, будут ли какие-то проблемы, но на данный момент, для дальнейшего обслуживания и управления должно быть намного проще, если у вас есть лучшее мнение или идеи, вы можете оставить комментарий или добавить меня vx:Lpyexplore333Обмен в частном порядке
Наконец, спасибо за терпение
Написать статью непросто. Надеюсь, вы оставите много комментариев и поделитесь своим мнением. Не забудьте нажатьотличный👍О~