mirror of
https://github.com/DCsunset/taskwarrior-webui.git
synced 2025-08-25 00:06:43 +02:00
feat(frontend): add basic task view
This commit is contained in:
parent
1589762009
commit
fbab994c10
5 changed files with 334 additions and 33 deletions
|
@ -1,15 +1,87 @@
|
||||||
<template>
|
<template>
|
||||||
<v-dialog v-model="showDialog" max-width="600px" persistent>
|
<v-dialog
|
||||||
|
v-model="showDialog"
|
||||||
|
max-width="600px"
|
||||||
|
persistent
|
||||||
|
@keydown.esc="closeDialog"
|
||||||
|
>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
{{ task ? 'Edit Task' : 'New Task' }}
|
{{ task ? 'Edit Task' : 'New Task' }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="formRef" lazy-validation>
|
||||||
|
<v-text-field
|
||||||
|
autofocus
|
||||||
|
v-model="formData.description"
|
||||||
|
label="Description*"
|
||||||
|
:rules="requiredRules"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<v-combobox
|
||||||
|
v-model="formData.project"
|
||||||
|
:items="projects"
|
||||||
|
hide-selected
|
||||||
|
label="Project"
|
||||||
|
/>
|
||||||
|
<v-text-field
|
||||||
|
v-model="formData.due"
|
||||||
|
:label="recur ? 'Due*' : 'Due'"
|
||||||
|
:rules="recur ? requiredRules : []"
|
||||||
|
:required="recur"
|
||||||
|
/>
|
||||||
|
<v-text-field
|
||||||
|
v-model="formData.wait"
|
||||||
|
label="Wait"
|
||||||
|
/>
|
||||||
|
<v-text-field
|
||||||
|
v-model="formData.until"
|
||||||
|
label="Until"
|
||||||
|
/>
|
||||||
|
<v-combobox
|
||||||
|
v-model="formData.tags"
|
||||||
|
:items="tags"
|
||||||
|
hide-selected
|
||||||
|
small-chips
|
||||||
|
multiple
|
||||||
|
label="Tags"
|
||||||
|
hint="Press tab or enter to add new tags"
|
||||||
|
/>
|
||||||
|
<v-radio-group v-model="formData.priority" row hide-details class="align-center">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<span class="mr-3 subtitle-1">
|
||||||
|
Priority
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<v-radio
|
||||||
|
v-for="p in priorities"
|
||||||
|
:key="p.text"
|
||||||
|
:label="p.text"
|
||||||
|
:value="p.value"
|
||||||
|
/>
|
||||||
|
</v-radio-group>
|
||||||
|
<v-row class="px-3">
|
||||||
|
<v-checkbox v-model="recur" class="mr-3" label="Recur" />
|
||||||
|
<v-text-field
|
||||||
|
label="period*"
|
||||||
|
v-model="formData.recur"
|
||||||
|
:rules="recur ? requiredRules : []"
|
||||||
|
:required="recur"
|
||||||
|
:disabled="!recur"
|
||||||
|
/>
|
||||||
|
</v-row>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<v-btn text @click="cloesDialog">
|
<v-btn text @click="closeDialog" width="80px">
|
||||||
Cancel
|
Cancel
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="primary" @click="submit">
|
<v-btn @click="reset" width="80px">
|
||||||
|
Reset
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="primary" @click="submit" width="80px">
|
||||||
Submit
|
Submit
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
@ -18,7 +90,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, reactive, ref } from '@vue/composition-api';
|
import { defineComponent, watch, computed, ref } from '@vue/composition-api';
|
||||||
import { Task } from 'taskwarrior-lib';
|
import { Task } from 'taskwarrior-lib';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -39,20 +111,94 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props: Props, context) {
|
setup(props: Props, context) {
|
||||||
|
const projects = computed(() => context.root.$store.getters.projects);
|
||||||
|
const tags = computed(() => context.root.$store.getters.tags);
|
||||||
|
|
||||||
const showDialog = computed({
|
const showDialog = computed({
|
||||||
get: () => props.value,
|
get: () => props.value,
|
||||||
set: val => context.emit('input', val)
|
set: val => context.emit('input', val)
|
||||||
});
|
});
|
||||||
const cloesDialog = () => {
|
|
||||||
showDialog.value = false;
|
const requiredRules = [
|
||||||
};
|
(str: string) => Boolean(str) || 'Required'
|
||||||
const submit = () => {
|
];
|
||||||
// TODO: submit
|
|
||||||
cloesDialog();
|
const recur = ref(Boolean(props.task?.recur));
|
||||||
|
const formData = ref({
|
||||||
|
description: '',
|
||||||
|
project: '',
|
||||||
|
due: '',
|
||||||
|
until: '',
|
||||||
|
wait: '',
|
||||||
|
tags: [] as string[],
|
||||||
|
priority: 'N',
|
||||||
|
recur: '',
|
||||||
|
...props.task
|
||||||
|
});
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
formData.value = {
|
||||||
|
description: '',
|
||||||
|
project: '',
|
||||||
|
due: '',
|
||||||
|
until: '',
|
||||||
|
wait: '',
|
||||||
|
tags: [] as string[],
|
||||||
|
priority: 'N',
|
||||||
|
recur: '',
|
||||||
|
...props.task
|
||||||
|
};
|
||||||
|
recur.value = Boolean(props.task?.recur);
|
||||||
|
(formRef.value as any).resetValidation();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.task, () => {
|
||||||
|
reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref(null);
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
showDialog.value = false;
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
const submit = async () => {
|
||||||
|
const valid = (formRef.value as any).validate();
|
||||||
|
if (valid) {
|
||||||
|
await context.root.$store.dispatch('updateTasks', [{
|
||||||
|
...formData.value,
|
||||||
|
project: formData.value.project || undefined,
|
||||||
|
due: formData.value.due || undefined,
|
||||||
|
until: formData.value.until || undefined,
|
||||||
|
wait: formData.value.wait || undefined,
|
||||||
|
priority: formData.value.priority === 'N' ? undefined : formData.value.priority,
|
||||||
|
recur: recur.value ? formData.value.recur : undefined
|
||||||
|
}]);
|
||||||
|
context.root.$store.commit('setNotification', {
|
||||||
|
color: 'success',
|
||||||
|
text: `Successfully ${props.task ? 'update' : 'create'} the task`
|
||||||
|
});
|
||||||
|
closeDialog();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const priorities = [
|
||||||
|
{ text: 'None', value: 'N' },
|
||||||
|
{ text: 'Low', value: 'L' },
|
||||||
|
{ text: 'Medium', value: 'M' },
|
||||||
|
{ text: 'High', value: 'H' }
|
||||||
|
];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cloesDialog,
|
requiredRules,
|
||||||
|
formRef,
|
||||||
|
tags,
|
||||||
|
projects,
|
||||||
|
priorities,
|
||||||
|
recur,
|
||||||
|
formData,
|
||||||
|
closeDialog,
|
||||||
|
reset,
|
||||||
submit,
|
submit,
|
||||||
showDialog
|
showDialog
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<TaskDialog v-model="showTaskDialog" :task="currentTask" />
|
<TaskDialog v-model="showTaskDialog" :task="currentTask" />
|
||||||
|
|
||||||
<v-btn-toggle v-model="status" mandatory>
|
<v-btn-toggle v-model="status" mandatory background-color="rgba(0, 0, 0, 0)">
|
||||||
<v-row class="pa-3">
|
<v-row class="pa-3">
|
||||||
<v-btn
|
<v-btn
|
||||||
v-for="st in allStatus"
|
v-for="st in allStatus"
|
||||||
|
@ -20,9 +20,9 @@
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{ st }}
|
{{ st }}
|
||||||
<v-badge
|
<v-badge
|
||||||
v-if="st === 'pending'"
|
v-if="st === 'pending' && classifiedTasks[st].length"
|
||||||
:content="classifiedTasks[st].length"
|
:content="classifiedTasks[st].length"
|
||||||
:color="st === status ? 'primary' : 'grey darken-1'"
|
:color="st === status ? 'primary' : 'grey'"
|
||||||
inline
|
inline
|
||||||
/>
|
/>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
@ -33,6 +33,7 @@
|
||||||
:items="classifiedTasks[status]"
|
:items="classifiedTasks[status]"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
show-select
|
show-select
|
||||||
|
item-key="uuid"
|
||||||
v-model="selected"
|
v-model="selected"
|
||||||
class="elevation-1"
|
class="elevation-1"
|
||||||
>
|
>
|
||||||
|
@ -47,9 +48,22 @@
|
||||||
small
|
small
|
||||||
dark
|
dark
|
||||||
title="Done"
|
title="Done"
|
||||||
|
@click="completeTasks(selected)"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-check</v-icon>
|
<v-icon>mdi-check</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
v-show="status === 'completed' || status === 'deleted'"
|
||||||
|
class="ma-1"
|
||||||
|
color="primary"
|
||||||
|
fab
|
||||||
|
dark
|
||||||
|
small
|
||||||
|
title="Restore"
|
||||||
|
@click="restoreTasks(selected)"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-restore</v-icon>
|
||||||
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
v-show="status !== 'deleted'"
|
v-show="status !== 'deleted'"
|
||||||
class="ma-1 red"
|
class="ma-1 red"
|
||||||
|
@ -89,17 +103,59 @@
|
||||||
</v-row>
|
</v-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="status === 'waiting'" v-slot:item.wait="{ item }">
|
||||||
|
{{ displayDate(item.wait) }}
|
||||||
|
</template>
|
||||||
|
<template v-slot:item.due="{ item }">
|
||||||
|
{{ displayDate(item.due) }}
|
||||||
|
</template>
|
||||||
|
<template v-slot:item.until="{ item }">
|
||||||
|
{{ displayDate(item.until) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:item.tags="{ item }">
|
||||||
|
<v-chip
|
||||||
|
v-for="tag in item.tags"
|
||||||
|
:key="tag"
|
||||||
|
small
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</v-chip>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-slot:item.actions="{ item }">
|
<template v-slot:item.actions="{ item }">
|
||||||
<v-icon
|
<v-icon
|
||||||
small
|
v-show="status === 'pending'"
|
||||||
class="mr-2"
|
size="20px"
|
||||||
|
class="ml-2"
|
||||||
|
@click="completeTasks([item])"
|
||||||
|
title="Done"
|
||||||
|
>
|
||||||
|
mdi-check
|
||||||
|
</v-icon>
|
||||||
|
<v-icon
|
||||||
|
v-show="status === 'completed' || status === 'deleted'"
|
||||||
|
size="20px"
|
||||||
|
class="ml-2"
|
||||||
|
@click="restoreTasks([item])"
|
||||||
|
title="Restore"
|
||||||
|
>
|
||||||
|
mdi-restore
|
||||||
|
</v-icon>
|
||||||
|
<v-icon
|
||||||
|
class="ml-2"
|
||||||
|
size="20px"
|
||||||
@click="editTask(item)"
|
@click="editTask(item)"
|
||||||
|
title="Edit"
|
||||||
>
|
>
|
||||||
mdi-pencil
|
mdi-pencil
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<v-icon
|
<v-icon
|
||||||
small
|
v-show="status !== 'deleted'"
|
||||||
|
class="ml-2"
|
||||||
|
size="20px"
|
||||||
@click="deleteTasks([item])"
|
@click="deleteTasks([item])"
|
||||||
|
title="Delete"
|
||||||
>
|
>
|
||||||
mdi-delete
|
mdi-delete
|
||||||
</v-icon>
|
</v-icon>
|
||||||
|
@ -113,6 +169,11 @@ import { defineComponent, computed, reactive, ref, ComputedRef, Ref } from '@vue
|
||||||
import { Task } from 'taskwarrior-lib';
|
import { Task } from 'taskwarrior-lib';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import TaskDialog from '../components/TaskDialog.vue';
|
import TaskDialog from '../components/TaskDialog.vue';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
function displayDate(str?: string) {
|
||||||
|
return str && moment(str).format('YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
[key: string]: unknown,
|
[key: string]: unknown,
|
||||||
|
@ -127,15 +188,6 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: Props, context) {
|
setup(props: Props, context) {
|
||||||
const headers = [
|
|
||||||
{ text: 'Project', value: 'project' },
|
|
||||||
{ text: 'Description', value: 'description' },
|
|
||||||
{ text: 'Priority', value: 'priority' },
|
|
||||||
{ text: 'Due', value: 'due' },
|
|
||||||
{ text: 'Tags', value: 'tags' },
|
|
||||||
{ text: 'Actions', value: 'actions', sortable: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
const selected = ref([] as Task[]);
|
const selected = ref([] as Task[]);
|
||||||
|
|
||||||
const status = ref('pending');
|
const status = ref('pending');
|
||||||
|
@ -147,31 +199,76 @@ export default defineComponent({
|
||||||
deleted: 'mdi-delete',
|
deleted: 'mdi-delete',
|
||||||
recurring: 'mdi-restart'
|
recurring: 'mdi-restart'
|
||||||
};
|
};
|
||||||
|
const headers = computed(() => [
|
||||||
|
{ text: 'Project', value: 'project' },
|
||||||
|
{ text: 'Description', value: 'description' },
|
||||||
|
{ text: 'Priority', value: 'priority' },
|
||||||
|
...(status.value !== 'waiting'
|
||||||
|
? [{ text: 'Due', value: 'due' }]
|
||||||
|
: [{ text: 'Wait', value: 'wait' }]),
|
||||||
|
{ text: 'Until', value: 'until' },
|
||||||
|
{ text: 'Tags', value: 'tags' },
|
||||||
|
{ text: 'Actions', value: 'actions', sortable: false }
|
||||||
|
]);
|
||||||
|
|
||||||
const tempTasks: { [key: string]: ComputedRef<Task[]> } = {};
|
const tempTasks: { [key: string]: ComputedRef<Task[]> } = {};
|
||||||
for (const status of allStatus) {
|
for (const status of allStatus) {
|
||||||
tempTasks[status] = computed((): Task[] => props.tasks?.filter(task => task.status === status));
|
tempTasks[status] = computed((): Task[] => props.tasks?.filter(task => task.status === status));
|
||||||
}
|
}
|
||||||
const classifiedTasks = reactive(tempTasks);
|
const classifiedTasks = reactive(tempTasks);
|
||||||
|
console.log(tempTasks);
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
context.root.$store.dispatch('fetchTasks');
|
context.root.$store.dispatch('fetchTasks');
|
||||||
};
|
};
|
||||||
|
|
||||||
const showTaskDialog = ref(false);
|
const showTaskDialog = ref(false);
|
||||||
const currentTask: Ref<Task> = ref(null);
|
const currentTask: Ref<Task | null> = ref(null);
|
||||||
const newTask = () => {
|
const newTask = () => {
|
||||||
showTaskDialog.value = true;
|
showTaskDialog.value = true;
|
||||||
currentTask.value = null;
|
currentTask.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const editTask = (_task: Task) => {
|
const editTask = (task: Task) => {
|
||||||
// TODO
|
showTaskDialog.value = true;
|
||||||
|
currentTask.value = _.cloneDeep(task);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteTasks = async (tasks: Task[]) => {
|
const deleteTasks = async (tasks: Task[]) => {
|
||||||
await context.root.$store.dispatch('deleteTasks', tasks);
|
await context.root.$store.dispatch('deleteTasks', tasks);
|
||||||
_.remove(selected.value, task => tasks.findIndex(t => t.uuid === task.uuid) !== -1);
|
selected.value = selected.value.filter(task => tasks.findIndex(t => t.uuid === task.uuid) === -1);
|
||||||
|
context.root.$store.commit('setNotification', {
|
||||||
|
color: 'success',
|
||||||
|
text: 'Successfully delete the task(s)'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const completeTasks = async (tasks: Task[]) => {
|
||||||
|
await context.root.$store.dispatch('updateTasks', tasks.map(task => {
|
||||||
|
return {
|
||||||
|
...task,
|
||||||
|
status: 'completed'
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
selected.value = selected.value.filter(task => tasks.findIndex(t => t.uuid === task.uuid) === -1);
|
||||||
|
context.root.$store.commit('setNotification', {
|
||||||
|
color: 'success',
|
||||||
|
text: 'Successfully complete the task(s)'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const restoreTasks = async (tasks: Task[]) => {
|
||||||
|
await context.root.$store.dispatch('updateTasks', tasks.map(task => {
|
||||||
|
return {
|
||||||
|
...task,
|
||||||
|
status: 'pending'
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
selected.value = selected.value.filter(task => tasks.findIndex(t => t.uuid === task.uuid) === -1);
|
||||||
|
context.root.$store.commit('setNotification', {
|
||||||
|
color: 'success',
|
||||||
|
text: 'Successfully restore the task(s)'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -186,8 +283,11 @@ export default defineComponent({
|
||||||
currentTask,
|
currentTask,
|
||||||
editTask,
|
editTask,
|
||||||
deleteTasks,
|
deleteTasks,
|
||||||
|
completeTasks,
|
||||||
|
restoreTasks,
|
||||||
showTaskDialog,
|
showTaskDialog,
|
||||||
TaskDialog
|
TaskDialog,
|
||||||
|
displayDate
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
|
<v-snackbar
|
||||||
|
v-model="snackbar"
|
||||||
|
:color="notification.color"
|
||||||
|
:timeout="4000"
|
||||||
|
>
|
||||||
|
{{ notification.text }}
|
||||||
|
|
||||||
|
<template v-slot:action="{ attrs }">
|
||||||
|
<v-btn
|
||||||
|
dark
|
||||||
|
text
|
||||||
|
v-bind="attrs"
|
||||||
|
@click="snackbar = false"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
|
|
||||||
<v-app-bar height="54px" fixed app>
|
<v-app-bar height="54px" fixed app>
|
||||||
<v-toolbar-title>
|
<v-toolbar-title>
|
||||||
Taskwarrior WebUI
|
Taskwarrior WebUI
|
||||||
|
@ -19,7 +38,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed } from '@vue/composition-api';
|
import { defineComponent, computed, onErrorCaptured } from '@vue/composition-api';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup(_props, context) {
|
setup(_props, context) {
|
||||||
|
@ -29,8 +48,38 @@ export default defineComponent({
|
||||||
context.root.$vuetify.theme.dark = val;
|
context.root.$vuetify.theme.dark = val;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const notification = computed(() => context.root.$store.state.notification);
|
||||||
|
const snackbar = computed({
|
||||||
|
get: () => context.root.$store.state.snackbar,
|
||||||
|
set: val => context.root.$store.commit('setSnackbar', val)
|
||||||
|
});
|
||||||
|
|
||||||
|
onErrorCaptured((err: any) => {
|
||||||
|
// axios error
|
||||||
|
let notification: any;
|
||||||
|
if (err?.response) {
|
||||||
|
const { status, data } = err.response!;
|
||||||
|
notification = {
|
||||||
|
color: 'error',
|
||||||
|
text: `Error ${status}: ${data}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const { name, message } = err as Error;
|
||||||
|
notification = {
|
||||||
|
color: 'error',
|
||||||
|
text: `Error ${name}: ${message}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
context.root.$store.commit('setNotification', notification);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dark
|
dark,
|
||||||
|
snackbar,
|
||||||
|
notification
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
5
frontend/package-lock.json
generated
5
frontend/package-lock.json
generated
|
@ -7788,6 +7788,11 @@
|
||||||
"minimist": "^1.2.5"
|
"minimist": "^1.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
||||||
|
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||||
|
},
|
||||||
"move-concurrently": {
|
"move-concurrently": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz",
|
"resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"@nuxtjs/pwa": "^3.0.0-beta.20",
|
"@nuxtjs/pwa": "^3.0.0-beta.20",
|
||||||
"@vue/composition-api": "^1.0.0-beta.1",
|
"@vue/composition-api": "^1.0.0-beta.1",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
"moment": "^2.27.0",
|
||||||
"nuxt": "^2.13.0",
|
"nuxt": "^2.13.0",
|
||||||
"nuxt-typed-vuex": "^0.1.19"
|
"nuxt-typed-vuex": "^0.1.19"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue