migration from vue2 to vue3 #405
5 changed files with 354 additions and 216 deletions
add categories customization
commit
89ca510897
|
|
@ -20,7 +20,9 @@
|
|||
namespace OCA\SideMenu\Controller;
|
||||
|
||||
use OCA\SideMenu\AppInfo\Application;
|
||||
use OCA\SideMenu\Service\Color;
|
||||
use OCA\SideMenu\Service\ConfigProxy;
|
||||
use OCA\SideMenu\Service\LangRepository;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
|
|
@ -30,7 +32,6 @@ use OCP\AppFramework\Http\RedirectResponse;
|
|||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
use OCA\SideMenu\Service\Color;
|
||||
|
||||
class AdminSettingController extends Controller
|
||||
{
|
||||
|
|
@ -41,6 +42,7 @@ class AdminSettingController extends Controller
|
|||
protected ConfigProxy $configProxy,
|
||||
protected IURLGenerator $urlGenerator,
|
||||
protected Color $color,
|
||||
protected LangRepository $langRepository,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
|
@ -65,6 +67,7 @@ class AdminSettingController extends Controller
|
|||
$excludedKeys = [
|
||||
'cache',
|
||||
'cache-categories',
|
||||
'langs',
|
||||
];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
|
|
@ -204,6 +207,8 @@ class AdminSettingController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$config['langs'] = $this->langRepository->getUsedLangs();
|
||||
|
||||
return new JSONResponse($config);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
<!--
|
||||
@license GNU AGPL version 3 or any later version
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<ul
|
||||
class="side-menu-setting-list"
|
||||
:class="{ hide: values.length === 0 }"
|
||||
>
|
||||
<li
|
||||
v-for="(item, key) in values"
|
||||
:key="key"
|
||||
class="side-menu-setting-list-item"
|
||||
@click="showEditForm(item)"
|
||||
>
|
||||
{{ item.en }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-add"
|
||||
@click="showAddForm"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
|
||||
<NcModal
|
||||
v-if="addForm"
|
||||
@close="hideAddForm"
|
||||
>
|
||||
<div class="modal__content">
|
||||
<div
|
||||
v-for="(lang, key) in langs"
|
||||
:key="key"
|
||||
>
|
||||
<span class="lang">{{ lang }}</span>
|
||||
<input
|
||||
v-model="newValue[lang]"
|
||||
type="text"
|
||||
required
|
||||
style="width: calc(100% - 100px)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-checkmark"
|
||||
@click="saveAdd"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
</div>
|
||||
</NcModal>
|
||||
|
||||
<NcModal
|
||||
v-if="editForm"
|
||||
@close="hideEditForm"
|
||||
>
|
||||
<div class="modal__content">
|
||||
<div
|
||||
v-for="(lang, key) in langs"
|
||||
:key="key"
|
||||
>
|
||||
<span class="lang">{{ lang }}</span>
|
||||
<input
|
||||
v-model="editValue[lang]"
|
||||
type="text"
|
||||
required
|
||||
style="width: calc(100% - 100px)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pull-right">
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-delete"
|
||||
@click="removeEdit"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
</div>
|
||||
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-checkmark"
|
||||
@click="saveEdit"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
</div>
|
||||
</NcModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NcModal, NcActions, NcActionButton } from '@nextcloud/vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const { langs, values } = defineProps(['lang', 'values'])
|
||||
|
||||
const input = ref(null)
|
||||
const values = ref([])
|
||||
const langs = ref([])
|
||||
const addForm = ref(false)
|
||||
const editForm = ref(false)
|
||||
const newValue = ref({})
|
||||
const editValue = ref({})
|
||||
|
||||
// mounted() {
|
||||
// this.input = document.querySelector('input[name="categories-custom"]')
|
||||
// this.init()
|
||||
// },
|
||||
// methods: {
|
||||
// init() {
|
||||
// this.values = JSON.parse(this.input.value)
|
||||
// this.langs = JSON.parse(this.input.getAttribute('data-langs'))
|
||||
// },
|
||||
// update() {
|
||||
// this.input.value = JSON.stringify(this.values)
|
||||
// },
|
||||
// showAddForm() {
|
||||
// this.newValue = { id: 'cat' + Math.random().toString().replace('0.', '') }
|
||||
//
|
||||
// this.addForm = true
|
||||
// },
|
||||
// showEditForm(value) {
|
||||
// this.editValue = { id: value.id }
|
||||
//
|
||||
// for (let i of this.langs) {
|
||||
// this.editValue[i] = typeof value[i] !== 'undefined' ? value[i] : ''
|
||||
// }
|
||||
//
|
||||
// this.editForm = true
|
||||
// },
|
||||
// saveAdd() {
|
||||
// for (let i of this.langs) {
|
||||
// if (!this.newValue[i] || /^\s*$/.test(this.newValue[i])) {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.values.push(this.newValue)
|
||||
// this.update()
|
||||
// this.hideAddForm()
|
||||
// this.newValue = {}
|
||||
// },
|
||||
// saveEdit() {
|
||||
// for (let i of this.langs) {
|
||||
// if (!this.editValue[i] || /^\s*$/.test(this.editValue[i])) {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for (let i in this.values) {
|
||||
// if (this.values[i].id === this.editValue.id) {
|
||||
// this.values[i] = this.editValue
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.update()
|
||||
// this.hideEditForm()
|
||||
// },
|
||||
// removeEdit() {
|
||||
// for (let i in this.values) {
|
||||
// if (this.values[i].id === this.editValue.id) {
|
||||
// this.values.splice(i, 1)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.update()
|
||||
// this.hideEditForm()
|
||||
// },
|
||||
// hideAddForm() {
|
||||
// this.addForm = false
|
||||
// },
|
||||
// hideEditForm() {
|
||||
// this.editForm = false
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.modal__content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.modal__content .lang {
|
||||
width: 60px;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
322
src/components/settings/form/FormAppCategory.vue
Normal file
322
src/components/settings/form/FormAppCategory.vue
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
<!--
|
||||
@license GNU AGPL version 3 or any later version
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<template>
|
||||
<NcButton
|
||||
aria-label="t('side_menu', 'Customize')"
|
||||
variant="primary"
|
||||
@click="openModal"
|
||||
>
|
||||
{{ t('side_menu', 'Customize') }}
|
||||
</NcButton>
|
||||
|
||||
<NcModal
|
||||
v-if="modal"
|
||||
_size="small"
|
||||
@close="closeModal"
|
||||
>
|
||||
<div class="modal__content">
|
||||
<div class="menu">
|
||||
<NcButton
|
||||
aria-label="t('side_menu', 'Categories')"
|
||||
:variant="section === 'cats' ? 'primary' : 'secondary'"
|
||||
@click="setSection('cats')"
|
||||
>
|
||||
{{ t('side_menu', 'Categories') }}
|
||||
</NcButton>
|
||||
<NcButton
|
||||
aria-label="t('side_menu', 'Applications')"
|
||||
:variant="section === 'apps' ? 'primary' : 'secondary'"
|
||||
@click="setSection('apps')"
|
||||
>
|
||||
{{ t('side_menu', 'Applications') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<div v-if="section === 'cats'">
|
||||
<table width="100%" v-if="!newCustomCategory && editCustomCategoryKey === null">
|
||||
<tbody>
|
||||
<tr v-for="(item, key) in categoriesCustom" :key="key">
|
||||
<td>{{ item[langs[0]] }}</td>
|
||||
<td width="50px">
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-edit"
|
||||
@click="editCustomCategory(key)"
|
||||
></NcActionButton>
|
||||
<NcActionButton
|
||||
icon="icon-delete"
|
||||
@click="removeCustomCategory(key)"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-else class="form">
|
||||
<template v-if="newCustomCategory">
|
||||
<NcTextField
|
||||
v-for="lang in langs"
|
||||
v-model="newCustomCategory[lang]"
|
||||
:label="lang"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="editCustomCategoryKey !== null">
|
||||
<NcTextField
|
||||
v-for="lang in langs"
|
||||
v-model="categoriesCustom[editCustomCategoryKey][lang]"
|
||||
:label="lang"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="section === 'apps'">
|
||||
<table width="100%">
|
||||
<tbody>
|
||||
<tr v-for="item in apps" :key="key">
|
||||
<td>
|
||||
<img
|
||||
:src="item.icon"
|
||||
:alt="item.name"
|
||||
/>
|
||||
{{ item.name }}
|
||||
</td>
|
||||
<td width="50%">
|
||||
<FormSelect
|
||||
v-model="appsCategoriesCustom[item.id]"
|
||||
:options="getOptions(categoriesCustom)"
|
||||
:required="false"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal__footer">
|
||||
<template v-if="section === 'cats'">
|
||||
<template v-if="newCustomCategory">
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-close"
|
||||
@click="cancelCustomCategory"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-checkmark"
|
||||
@click="saveCustomCategory"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
</template>
|
||||
<template v-if="editCustomCategoryKey !== null">
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
icon="icon-close"
|
||||
@click="cancelCustomCategory"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<NcActions>
|
||||
<NcActionButton
|
||||
v-if="!newCustomCategory && editCustomCategoryKey === null"
|
||||
@click="addCustomCategory"
|
||||
icon="icon-add"
|
||||
></NcActionButton>
|
||||
<NcActionButton
|
||||
v-if="editCustomCategoryKey !== null"
|
||||
@click="saveCustomCategory"
|
||||
icon="icon-checkmark"
|
||||
></NcActionButton>
|
||||
</NcActions>
|
||||
</template>
|
||||
</template>
|
||||
<NcButton
|
||||
variant="primary"
|
||||
@click="closeModal"
|
||||
class="btn-close"
|
||||
>
|
||||
{{ t('side_menu', 'Close') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
</NcModal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NcButton, NcModal, NcActions, NcActionButton, NcTextField } from '@nextcloud/vue'
|
||||
import { useNavStore } from '../../../store/nav.js'
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import FormSelect from './FormSelect'
|
||||
|
||||
const emit = defineEmits(['update:categoriesCustom', 'update:appsCategoriesCustom'])
|
||||
const { categoriesCustom, appsCategoriesCustom, langs } = defineProps({
|
||||
categoriesCustom: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
appsCategoriesCustom: {
|
||||
type: [Object, Array],
|
||||
required: true,
|
||||
},
|
||||
langs: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const navStore = useNavStore()
|
||||
const modal = ref(false)
|
||||
const apps = ref([])
|
||||
const categories = ref([])
|
||||
const section = ref('apps')
|
||||
const newCustomCategory = ref(null)
|
||||
const editCustomCategoryKey = ref(null)
|
||||
const appsCategoriesCustomNext = ref({})
|
||||
|
||||
const openModal = () => {
|
||||
modal.value = true
|
||||
}
|
||||
|
||||
const closeModal = () => {
|
||||
modal.value = false
|
||||
}
|
||||
|
||||
const setSection = (value) => {
|
||||
section.value = value
|
||||
}
|
||||
|
||||
const addCustomCategory = () => {
|
||||
let data = {
|
||||
id: 'cat' + Math.random().toString().replace('0.', ''),
|
||||
}
|
||||
|
||||
langs.forEach((lang) => {
|
||||
data[lang] = ''
|
||||
})
|
||||
|
||||
newCustomCategory.value = data
|
||||
}
|
||||
|
||||
const cancelCustomCategory = () => {
|
||||
newCustomCategory.value = null
|
||||
editCustomCategoryKey.value = null
|
||||
}
|
||||
|
||||
const saveCustomCategory = () => {
|
||||
const data = categoriesCustom
|
||||
|
||||
if (editCustomCategoryKey.value === null) {
|
||||
data.push({...newCustomCategory.value})
|
||||
}
|
||||
|
||||
emit('update:categoriesCustom', data)
|
||||
|
||||
newCustomCategory.value = null
|
||||
editCustomCategoryKey.value = null
|
||||
}
|
||||
|
||||
const removeCustomCategory = (key) => {
|
||||
const data = categoriesCustom
|
||||
delete data[key]
|
||||
|
||||
emit('update:categoriesCustom', Object.values(data))
|
||||
}
|
||||
|
||||
const editCustomCategory = (key) => {
|
||||
editCustomCategoryKey.value = key
|
||||
}
|
||||
|
||||
const getOptions = (custom) => {
|
||||
const data = []
|
||||
|
||||
custom.forEach((item) => {
|
||||
data.push({id: item.id, label: item[langs[0]]})
|
||||
})
|
||||
|
||||
categories.value.forEach((item) => {
|
||||
data.push({id: item.categoryId, label: item.name !== '' ? item.name : t('side_menu', 'Other')})
|
||||
})
|
||||
|
||||
data.sort((a, b) => (a.label < b.label ? -1 : 1))
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
watch(() => appsCategoriesCustomNext, (value) => {
|
||||
console.log(value)
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
apps.value = await navStore.getApps()
|
||||
categories.value = await navStore.getCategories()
|
||||
|
||||
let value = {}
|
||||
|
||||
apps.value.forEach((app) => {
|
||||
if (!appsCategoriesCustom[app.id]) {
|
||||
value[app.id] = null
|
||||
} else {
|
||||
value[app.id] = appsCategoriesCustom[app.id]
|
||||
}
|
||||
})
|
||||
|
||||
emit('update:appsCategoriesCustom', value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal__content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.menu button {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.modal__footer {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.modal__footer button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
tr:hover, td:hover {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
v-model="model"
|
||||
:multiple="multiple"
|
||||
>
|
||||
<option :value="null" v-if="!required"></option>
|
||||
<option
|
||||
v-for="option in options"
|
||||
:key="option.id"
|
||||
|
|
@ -49,12 +50,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
<script setup>
|
||||
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
||||
|
||||
const model = defineModel({ type: [Number, String, Array] })
|
||||
const model = defineModel({ type: [Number, String, Array, null] })
|
||||
const { options, expanded } = defineProps({
|
||||
options: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
expanded: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
|
|
|||
|
|
@ -196,6 +196,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</TableValue>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableLabel
|
||||
label="Customize application categories"
|
||||
:top="true"
|
||||
>
|
||||
</TableLabel>
|
||||
<TableValue>
|
||||
<FormAppCategory
|
||||
:langs="config['langs']"
|
||||
:categories-custom="config['categories-custom']"
|
||||
:apps-categories-custom="config['apps-categories-custom']"
|
||||
|
||||
@update:categories-custom="(value) => (config['categories-custom'] = value)"
|
||||
@update:apps-categories-custom="(value) => (config['apps-categories-custom'] = value)"
|
||||
/>
|
||||
</TableValue>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableLabel
|
||||
label="Customize sorting"
|
||||
|
|
@ -565,6 +583,7 @@ import FormAppPicker from '../components/settings/form/FormAppPicker'
|
|||
import FormAppSort from '../components/settings/form/FormAppSort'
|
||||
import FormCatSort from '../components/settings/form/FormCatSort'
|
||||
import FormDisplayPicker from '../components/settings/form/FormDisplayPicker'
|
||||
import FormAppCategory from '../components/settings/form/FormAppCategory'
|
||||
|
||||
const menu = [
|
||||
{ label: 'Global', section: 'global' },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue