123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730 |
- <template>
- <v-container class="spacing-playground" fluid>
- <v-row no-gutters>
- <v-col cols="8" class="col detail__main pa-6">
- <treeselect
- placeholder="Категория"
- value="id"
- noChildrenText="Нет подкатегорий"
- valueFormat="object"
- v-model="category"
- :options="treeCategories"
- class="tree-select"
- />
- <div class="error" v-if="$v.$dirty && !$v.category.required">
- Обязательное поле
- </div>
- <v-text-field
- v-model="title"
- label="Заголовок"
- @blur="getAlias"
- v-if="category"
- >
- </v-text-field>
- <div class="error" v-if="$v.$dirty && !$v.title.required">
- Обязательное поле
- </div>
- <v-text-field v-model="generatedAlias" disabled label="Алиас">
- </v-text-field>
- <v-combobox
- v-model="moduleId"
- label="Модуль"
- required
- :items="modules"
- item-text="name"
- item-value="id"
- @click="getContainer('module')"
- ></v-combobox>
- <v-text-field v-model="sliderId" label="Слайдер"> </v-text-field>
- <v-combobox
- v-model="sectionId"
- label="Секция"
- required
- :items="sections"
- item-text="name"
- item-value="id"
- @click="getContainer('section')"
- :disabled="$v.generatedAlias.invalid"
- :class="!$v.generatedAlias.invalid ? '' : 'green'"
- >
- </v-combobox>
- <v-text-field v-model="sorting" label="Позиция"> </v-text-field>
- <v-textarea
- name="input-7-1"
- label="Описание"
- hint=""
- v-model="description"
- ></v-textarea>
- <div class="error" v-if="$v.$dirty && !$v.description.required">
- Обязательное поле
- </div>
- <v-textarea
- label="Видео"
- v-model="video"
- rows="3"
- auto-grow
- ></v-textarea>
- <v-text-field
- v-model="externalLink"
- label="Внешняя ссылка"
- ></v-text-field>
- <v-file-input
- prepend-icon=""
- append-icon="$file"
- label="Прикрепить файлы (PDF, ZIP, DOC, DOCX)"
- chips
- multiple
- small-chips
- v-model="filesModel"
- @change="loadFiles"
- accept=".pdf, .zip, .doc, .docx"
- ></v-file-input>
- <v-chip-group v-if="files.length">
- <v-chip
- @click:close="deleteFile(file, index)"
- v-for="(file, index) in files"
- :key="file.id"
- close
- >
- {{ file.original_file_name }}
- </v-chip>
- </v-chip-group>
- <AdminEditor :value="content" @input="content = $event"></AdminEditor>
- <div class="error" v-if="$v.$dirty && !$v.content.minLength">
- Обязательное поле
- </div>
- <v-btn
- class="ml-auto mr-0 pa-6 my-6"
- @click="publishNews"
- v-if="previewID"
- >Опубликовать</v-btn
- >
- <v-btn class="ml-auto mr-0 pa-6 my-6" @click="createNews" v-else
- >Создать публикацию</v-btn
- >
- <v-btn class="ml-auto mr-0 pa-6 my-6" @click="previewNews"
- >Предпросмотр</v-btn
- >
- <v-btn class="ml-auto mr-0 pa-6 my-6" @click="shareNews"
- >Поделиться</v-btn
- >
- </v-col>
- <v-col cols="3" class="col detail__sidebar pa-6">
- <v-img
- :lazy-src="url(img ? img.relative_path : '')"
- :src="url(img ? img.relative_path : '')"
- :max-width="400"
- ></v-img>
- <!-- <v-text-field v-model="photoTitle" label="Источник фото" placeholder="Чтобы вставить картинку заполните это поле"> </v-text-field>-->
- <v-file-input
- v-model="file"
- @change="fileHandler('main-img', 814, 458)"
- label="Картинка:"
- type="file"
- accept="image/*"
- ref="input1"
- :disabled="!$v.generatedAlias.required"
- :class="!$v.generatedAlias.required ? '' : ''"
- ></v-file-input>
- <v-img
- :lazy-src="url(previewImg ? previewImg.relative_path : '')"
- :max-width="400"
- :src="url(previewImg ? previewImg.relative_path : '')"
- ></v-img>
- <v-file-input
- v-model="previewFile"
- @change="fileHandler('preview-img', 850, 420)"
- label="Превью картинка:"
- type="file"
- accept="image/*"
- ref="inputPreview"
- :disabled="!$v.generatedAlias.required"
- ></v-file-input>
- <v-select
- class="tree-select"
- :items="authorsList"
- item-text="name"
- item-value="value"
- v-model="author"
- label="Автор"
- ></v-select>
- <v-checkbox label="Показывать автора" v-model="showAuthor">
- </v-checkbox>
- <v-checkbox label="Не отдавать в Яндекс" v-model="dont_send_to_rss">
- </v-checkbox>
- <v-checkbox label="МОЛНИЯ" v-model="lightning"> </v-checkbox>
- <v-checkbox label="Включить комментарии" v-model="allowComments">
- </v-checkbox>
- <v-checkbox
- label="На правах рекламы"
- v-model="advertisement"
- ></v-checkbox>
- <v-checkbox label="Партнерский материал" v-model="partner">
- </v-checkbox>
- <v-checkbox
- label="Имеются противопоказания"
- v-model="contraindications"
- >
- </v-checkbox>
- {{ properties }}
- <v-checkbox v-model="activity" label="Активность"> </v-checkbox>
- <v-text-field v-model="hash_tags" label="Хэш тэги"> </v-text-field>
- <v-img :src="url(storieImg ? storieImg.relative_path : '')"></v-img>
- <v-checkbox v-model="showInStories" label="Показывать в сторис">
- </v-checkbox>
- <v-checkbox v-model="verified" label="Проверено">
- </v-checkbox>
- <!--<v-file-input
- label="Картинка для истории:"
- type="file"
- accept="image/*"
- ref="inputStorie"
- v-model="storieFile"
- :disabled="!$v.generatedAlias.required"
- :class="!$v.generatedAlias.required ? '' : ''"
- @change="fileHandler('storie-img', 800, 1200)"
- ></v-file-input>-->
- <v-menu
- ref="menu"
- :close-on-content-click="false"
- :return-value.sync="startActivity.date"
- transition="scale-transition"
- offset-y
- min-width="auto"
- >
- <template v-slot:activator="{ on, attrs }">
- <v-text-field
- v-model="startActivity.date"
- label="Выберите дату"
- prepend-icon="mdi-calendar"
- readonly
- v-bind="attrs"
- v-on="on"
- ></v-text-field>
- </template>
- <v-date-picker v-model="startActivity.date" no-title scrollable>
- <v-spacer></v-spacer>
- <v-btn text color="primary" @click="menu = false"> Cancel </v-btn>
- <v-btn
- text
- color="primary"
- @click="$refs.menu.save(startActivity.date)"
- >
- OK
- </v-btn>
- </v-date-picker>
- </v-menu>
- <vue-timepicker
- v-model="startActivity.time"
- format="HH:mm:ss"
- ></vue-timepicker>
- <div>Выбранное время: {{ start_activity }}</div>
- <div class="error" v-if="$v.$dirty && !$v.start_activity.required">
- Обязательное поле
- </div>
- <v-text-field v-model="sliderId" label="Слайдер"> </v-text-field>
- <v-text-field v-model="source" label="Источник" @blur="getAlias">
- </v-text-field>
- <v-text-field v-model="metaDescription" label="Мета описание для SEO">
- </v-text-field>
- <v-text-field v-model="metaKeys" label="Мета ключи для SEO">
- </v-text-field>
- </v-col>
- <div v-if="message.length" class="message-box">
- <v-alert
- class="message"
- dismissible
- v-model="message"
- :type="messageClass ? messageClass : 'primary'"
- >
- {{ message }}
- </v-alert>
- </div>
- </v-row>
- </v-container>
- </template>
- <script>
- import { required, minLength } from "vuelidate/lib/validators";
- import VueTimepicker from "vue2-timepicker";
- import "vue2-timepicker/dist/VueTimepicker.css";
- import Treeselect from "@riophae/vue-treeselect";
- import "@riophae/vue-treeselect/dist/vue-treeselect.css";
- const nest = (items, id = null, link = "parent_id") => {
- return items
- .filter((item) => item[link] === id)
- .map((item) => ({
- ...item,
- label: item.title,
- children: nest(items, item.id),
- }));
- };
- export default {
- components: {
- VueTimepicker,
- Treeselect,
- },
- methods: {
- createNews() {
- // Проверяем валидацию инпутов
- this.$v.$touch();
- if (!this.$v.$invalid) {
- this.$axios
- .post("admin/news/create", this.newsItem)
- .then((res) => {
- res.data.success == true;
- this.message = "Новость создана";
- setTimeout(() => {
- console.log(this.$route);
- this.$router.push("/admin/publications");
- }, 500);
- })
- .catch((err) => {
- console.dir(err);
- this.notificationHandler(err.message, true);
- return " ";
- });
- }
- },
- previewNews() {
- this.$v.$touch();
- if (this.$v.$invalid) return;
- const path = this.isVideo ? '/preview/video' : '/preview';
- let { href } = this.$router.resolve({ path });
- this.$axios
- .post("admin/news/preview", this.newsItem)
- .then((res) => {
- let newsPreview = JSON.stringify(res.data.data);
- window.localStorage.setItem("preview", newsPreview);
- window.open(href, "_blank");
- })
- .catch((err) => this.notificationHandler(err.message, true));
- },
- shareNews() {
- this.$v.$touch();
- if (this.$v.$invalid) return;
- this.activity = false;
- this.$axios.post("admin/news/create", this.newsItem).then((res) => {
- this.previewID = res.data.data.news_id;
- const path = this.isVideo ?
- `/preview/video?uuid=${this.previewID}` :
- `/preview?uuid=${this.previewID}`;
- let { href } = this.$router.resolve({ path });
- window.open(href, "_blank");
- });
- },
- publishNews() {
- this.$v.$touch();
- if (this.$v.$invalid) return;
- this.$axios.post("admin/news/update", {
- ...this.newsItem,
- id: this.previewID,
- });
- -setTimeout(() => {
- this.message = "Новость опубликована";
- this.$router.push("/admin/publications");
- }, 500);
- },
- getAlias() {
- this.$axios
- .post("admin/alias/generation", { text: this.title }) // сначала генерируем алиас
- .then((res) => {
- if (res.status == 200) {
- this.generatedAlias = res.data.data.text;
- this.$axios // проверяем его доступность, если все ок - отдаем алиас без возможности редактировать
- .get(
- "admin/alias/check/news/" +
- this.generatedAlias +
- "/" +
- this.category.id
- )
- .then((res) => {
- if (res.success) {
- this.alias = this.generatedAlias;
- return generatedAlias;
- }
- })
- .catch((err) => {
- this.notificationHandler(err.errors.join("* "), true);
- return err;
- });
- }
- return res;
- });
- },
- // Файл хэндлер. Для каждого отдельные методы
- getFile(type) {
- if (type == "main-img") {
- return this.file;
- } else if (type == "preview-img") {
- return this.previewFile;
- } else if (type == "storie-img") {
- return this.storieFile;
- }
- },
- notificationHandler(message, error) {
- this.messageClass = "";
- this.message = message;
- if (error) {
- this.messageClass = "error";
- }
- const interva = setTimeout(() => {
- this.message = "";
- }, 2000);
- },
- fileHandler(type, width, height) {
- const fileData = new FormData();
- fileData.append("file", this.getFile(type));
- console.log(this.getFile(type));
- if (type == "main-img" && this.category.alias == "articles") {
- fileData.append("destination_width", 1900);
- fileData.append("destination_height", 800);
- } else {
- fileData.append("destination_width", width);
- fileData.append("destination_height", height);
- }
- fileData.append("title", "example");
- // TODO - добавить поле для названия фотографии. Создать метод - либо добавлять после создания, либо во время, либо до
- this.$axios
- .$post("authorized/admin/files/upload/image/news/raw", fileData, {
- headers: {
- "Content-Type": `multipart/form-data;`,
- },
- })
- .then((res) => {
- if (type == "main-img") {
- this.img = res.data;
- } else if (type == "preview-img") {
- this.previewImg = res.data;
- } else if (type == "storie-img") {
- this.storieImg = res.data;
- }
- console.log(res, ".THEN");
- if (res.success) {
- this.notificationHandler("Картинка удачно загрузилась!", false);
- } else {
- this.notificationHandler(res.errors.join("* "), true);
- }
- return res;
- })
- .catch((err) => {
- console.log(err, ".CATCH");
- this.notificationHandler(err.errors.join("* "), true);
- return err;
- });
- },
- getContainer(str) {
- this.$axios.get("admin/containers/type_" + str).then((res) => {
- if (str == "module") {
- this.modules = res.data.data;
- return res.data;
- } else if (str == "section") {
- this.sections = res.data.data;
- return res.data;
- } else if (str == "slider") {
- this.sliders = res.data.data;
- return res.data;
- }
- });
- },
- async getCategory() {
- await this.$axios.$get("/admin/categories").then((res) => {
- console.log(res.data);
- this.categories = res.data;
- return res.data;
- });
- },
- async getAuthors() {
- await this.$axios
- .$post("authorized/admin/user/profiles/list/by_group", {
- groups: [3],
- })
- .then((res) => {
- console.log(res.data);
- if (!res.data.users) return [];
- this.authors = res.data.users.map((item) => ({
- value: item.user_id,
- name: item.first_name,
- }));
- return this.authors;
- });
- },
- url(relative_path) {
- let url = "https://api.amic.ru/" + relative_path;
- return url;
- },
- async loadFile(File) {
- const fileData = new FormData();
- fileData.append("file", File);
- fileData.append("title", File.name);
- return await this.$axios
- .post("authorized/admin/files/upload/document/news", fileData)
- .then((res) => res.data.data)
- .catch((e) => console.log(e));
- },
- async deleteFile(file, index) {
- this.files.splice(index, 1);
- await this.$axios
- .get(`authorized/admin/files/delete/image/${file.id}`)
- .catch((e) => console.log(e));
- },
- async loadFiles(files) {
- if (!files) return;
- const promises = files.map((file) => this.loadFile(file));
- const newFiles = await Promise.all(promises);
- this.files = [...this.files, ...newFiles];
- this.filesModel = null;
- },
- },
- data() {
- return {
- sliderId: "",
- messageClass: "",
- menu: "",
- message: "",
- author: "",
- authors: [],
- activity: true,
- categories: [],
- dont_send_to_rss: false,
- photoTitle: "",
- hash_tags: "",
- sorting: null,
- showAuthor: true,
- allowComments: true,
- advertisement: false,
- partner: false,
- contraindications: false,
- propertiesList: [],
- modules: [],
- sliders: [],
- sections: [],
- metaDescription: "",
- sectionId: "",
- moduleId: "",
- title: "",
- description: "",
- category: null,
- metaKeys: " ",
- showInStories: false,
- storieImg: {},
- img: {},
- source: "",
- previewImg: {},
- previewFile: null,
- storieFile: null,
- lightning: false,
- file: null,
- active: true,
- alias: "",
- verified: false,
- generatedAlias: "",
- startActivity: {
- date: "",
- time: "00:00:00",
- },
- previewID: null,
- content: "",
- video: "",
- externalLink: "",
- files: [],
- filesModel: null,
- };
- },
- validations: {
- category: {
- required,
- },
- generatedAlias: {
- required,
- },
- start_activity: {
- required,
- },
- content: {
- minLength: minLength(10),
- },
- description: {
- required,
- },
- title: { required },
- },
- layout: "admin",
- mounted() {
- this.$axios
- .get("admin/properties", {
- headers: { AccessToken: this.$auth.strategy.token.get() },
- })
- .then((res) => (this.propertiesList = res.data.data));
- window.addEventListener("beforeunload", (event) => {
- event.preventDefault();
- event.returnValue = "";
- });
- this.getCategory();
- this.getAuthors();
- },
- computed: {
- isVideo() {
- if(!this.category) return false;
- const parentCategory = this.category.parent_for_admin;
- return parentCategory && parentCategory.alias == 'video';
- },
- start_activity() {
- return this.startActivity.date + " " + this.startActivity.time;
- },
- properties() {
- const entries = this.propertiesList.map((p) => [p.id, this[p.code]]);
- return Object.fromEntries(entries);
- },
- newsItem() {
- return {
- author: this.author,
- activity: this.activity,
- dont_send_to_rss: this.dont_send_to_rss,
- hash_tags: this.hash_tags,
- title: this.title,
- text: this.content,
- alias: this.generatedAlias,
- description: this.description,
- news_category_id: this.category.id,
- start_activity: this.start_activity,
- detail_image_id: this.img.image_id ? this.img.image_id : "",
- preview_image_id: this.previewImg.image_id
- ? this.previewImg.image_id
- : "",
- source: this.source,
- slider_id: this.sliderId.length ? this.sliderId : "",
- section_id: this.sectionId.id ? this.sectionId.id : "",
- meta_description: this.metaDescription,
- show_in_stories: this.showInStories,
- module_id: this.moduleId.id ? this.moduleId.id : "",
- meta_key: this.metaKeys,
- allow_comments: this.allowComments,
- show_author: this.showAuthor,
- sorting: this.sorting,
- lightning: this.lightning,
- story_file_id: this.storieImg ? this.storieImg.id : "",
- video: this.video,
- link: this.externalLink,
- properties: this.properties,
- verified: this.verified,
- files: this.files.map((file) => file.id),
- };
- },
- treeCategories() {
- if (!this.categories) return [];
- return nest(this.categories);
- },
- authorsList() {
- return this.authors;
- },
- },
- async beforeRouteLeave (to, from, next) {
- const out = window.confirm("Вы уверены, что хотите покинуть страницу?");
- if(!out) return;
- next();
- },
- };
- </script>
- <style lang="less">
- .tree-select {
- margin-bottom: 15px;
- }
- .v-application .error {
- margin-top: -5px;
- opacity: 0.8;
- background: transparent !important;
- }
- .green {
- border: 1px solid green;
- }
- input {
- background: none !important;
- border: none !important;
- }
- .message {
- width: 400px;
- height: 100px;
- text-align: center;
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 1px 1px 1px 1px;
- background: white;
- &-box {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- right: 20px;
- bottom: 20px;
- position: fixed;
- justify-content: flex-end;
- }
- }
- </style>
|