commit d222e7d7c13f386247b3e06502f93956dee3db61 Author: Vladimir Date: Sat May 31 19:30:53 2025 +0300 Init git diff --git a/.env b/.env new file mode 100644 index 0000000..625d50d --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +NODE_ENV=development +VUE_PUBLIC_PATH=/complexes-frame +VUE_APP_API_URL=https://dev.joywork.ru \ No newline at end of file diff --git a/.env.development.example b/.env.development.example new file mode 100644 index 0000000..c5a8716 --- /dev/null +++ b/.env.development.example @@ -0,0 +1,3 @@ +NODE_ENV=local +VUE_PUBLIC_PATH=/complexes-frame +VUE_APP_API_URL=http://localhost \ No newline at end of file diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000..c5a8716 --- /dev/null +++ b/.env.local.example @@ -0,0 +1,3 @@ +NODE_ENV=local +VUE_PUBLIC_PATH=/complexes-frame +VUE_APP_API_URL=http://localhost \ No newline at end of file diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..60c5af4 --- /dev/null +++ b/.env.production.example @@ -0,0 +1,3 @@ +NODE_ENV=production +VUE_PUBLIC_PATH=/complexes-frame +VUE_APP_API_URL=https://joywork.ru \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f3dd868 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +.DS_Store +node_modules +/dist + + +# env files +.env.local +.env.*.local +.env.development +.env.production + + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..9b13f71 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# complexes + +## Project setup +``` +npm install +``` + +### Compiles and hot-reloads for development +``` +npm run serve +``` + +### Compiles and minifies for production +``` +npm run build +``` + +### Customize configuration +See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..e955840 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..4aafc5f --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "esnext", + "baseUrl": "./", + "moduleResolution": "node", + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a1d326d --- /dev/null +++ b/package.json @@ -0,0 +1,81 @@ +{ + "name": "complexes", + "version": "0.1.0", + "private": true, + "scripts": { + "serve": "VUE_APP_IS_SERVE=true vue-cli-service serve --mode local", + "serve:dev": "VUE_APP_IS_SERVE=true vue-cli-service serve --mode development", + "serve:prod": "VUE_APP_IS_SERVE=true vue-cli-service serve --mode production", + "build": "vue-cli-service build --mode local", + "build:dev": "vue-cli-service build --mode development", + "build:prod": "vue-cli-service build --mode production" + }, + "dependencies": { + "@rushstack/eslint-patch": "^1.6.1", + "@vue/eslint-config-prettier": "^9.0.0", + "@vuelidate/core": "^2.0.3", + "@vuelidate/validators": "^2.0.4", + "axios": "^1.6.5", + "core-js": "^3.8.3", + "i": "^0.3.7", + "lodash.debounce": "^4.0.8", + "moment-timezone": "^0.5.46", + "npm": "^10.5.2", + "ol": "^8.2.0", + "pinia": "^2.1.7", + "primevue": "3.32", + "quill": "^2.0.0", + "sass": "^1.69.7", + "tiny-emitter": "^2.1.0", + "vue": "^3.2.13", + "vue-cropperjs": "^5.0.0", + "vue-dadata": "^3.0.0-beta.13", + "vue-router": "^4.0.3", + "vue-yandex-maps": "^2.1.3", + "vue3-perfect-scrollbar": "^2.0.0", + "vuedraggable": "^4.1.0", + "vuex": "^4.0.0" + }, + "devDependencies": { + "@types/vue-cropperjs": "^4.1.6", + "@vue/cli-plugin-babel": "~5.0.0", + "@vue/cli-plugin-router": "~5.0.0", + "@vue/cli-plugin-vuex": "~5.0.0", + "@vue/cli-service": "~5.0.0", + "autoprefixer": "^10.4.16", + "eslint": "^8.56.0", + "eslint-config-prettier": "^8.0.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-vue": "^9.19.2", + "postcss": "^8.4.33", + "prettier": "3.1.1", + "sass-loader": "^13.3.3", + "tailwindcss": "^3.4.1", + "vue-cli-plugin-tailwind": "~3.0.0" + }, + "eslintConfig": { + "root": true, + "env": { + "node": true + }, + "extends": [ + "plugin:vue/vue3-essential", + "eslint:recommended", + "@vue/typescript/recommended", + "plugin:prettier/recommended" + ], + "parserOptions": { + "ecmaVersion": 2020 + }, + "rules": { + "no-undef": "off", + "vue/multi-word-component-names": "off" + } + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead", + "not ie 11" + ] +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..b8c209c --- /dev/null +++ b/public/index.html @@ -0,0 +1,21 @@ + + + + + + + + + + JoyWork + + + +
+ + + diff --git a/src/app/App.vue b/src/app/App.vue new file mode 100644 index 0000000..5d84bfc --- /dev/null +++ b/src/app/App.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/src/app/images/rocket-spinner.svg b/src/app/images/rocket-spinner.svg new file mode 100644 index 0000000..913d1c8 --- /dev/null +++ b/src/app/images/rocket-spinner.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/app/router/index.js b/src/app/router/index.js new file mode 100644 index 0000000..76a832d --- /dev/null +++ b/src/app/router/index.js @@ -0,0 +1,97 @@ +import { createRouter, createWebHashHistory, createWebHistory, RouterLink, RouterView } from 'vue-router' +//import HomeView from '../views/HomeView.vue' + +const routes = [ + { + path: "/:token/view", + name: "view-pages", + component: () => import("@/pages/View"), + meta: { + isViewPage: true, + }, + children: [ + { + path: '', + name: 'complexes-list', + component: () => import('@/pages/ComplexesView.vue'), + }, + { + path: ":id", + name: "complex-homes-router", + // component: () => import("@/pages/ComplexViewMode"), + component: RouterView, + children: [ + { + path: '', + name: 'complex-homes', + component: () => import("@/pages/ComplexViewMode"), + }, + { + path: 'apartments', + name: 'complex-apartments', + component: () => import('@/pages/ApartmentsList.vue') + }, + { + path: "home/:house_id", + name: "home", + component: () => import("@/pages/HomePage.vue"), + children: [ + { + path: "", + name: "view-home", + component: () => import("@/pages/HomePage/ViewHome.vue"), + }, + { + path: "apartment/:apartment_id", + name: "apartment-detail", + component: () => import("@/pages/HomePage/ViewHome.vue"), + } + // { + // path: "apartment-view/:apartment_id", + // name: "apartment-view", + // component: () => import("@/shared/layouts/ViewApartment.vue") + // } + ], + } + ] + }, + ], + }, + { + path: "/archived-objects", + name: "archived-objects-page", + component: () => import("@/pages/Edit"), + children: [ + { + path: "", + name: "archive-main", + component: () => import("@/pages/MainPage.vue"), + }, + ] + }, + { + path: "/apartments/:apartment_id", + name: "apartment", + component: () => import("@/pages/Apartment"), + }, + { + path: "/:catchAll(.*)", + // redirect: { path: '/view' } + }, + { + path:"/test-layout", + component: () => import("@/shared/layouts/TestLayout.vue"), + }, + { + path: "/access-denied", + component: () => import("@/shared/layouts/AccessDenied.vue"), + } +] + +const router = createRouter({ + history: createWebHashHistory('/complexes-iframe/'), + + routes +}) + +export default router diff --git a/src/app/router/index.old.js b/src/app/router/index.old.js new file mode 100644 index 0000000..ea2073b --- /dev/null +++ b/src/app/router/index.old.js @@ -0,0 +1,101 @@ +import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router' +//import HomeView from '../views/HomeView.vue' + +const routes = [ + { + path: "/", + name: "main", + component: () => import("@/pages/MainPage.vue"), + }, + { + path: "/view", + name: "complexes-view", + component: () => import("@/pages/ComplexesViewWrapper.vue"), + children: [ + { + path: '', + name: 'complexes-view-list', + component: () => import('@/pages/ComplexesView.vue'), + }, + { + path: ":id", + name: "complexView", + component: () => import("@/pages/ComplexViewMode"), + }, + { + path: "home/:id", + name: "complexes-view-home", + component: () => import("@/pages/HomePage.vue"), + children: [ + { + path: "view-home", + name: "complexes-view-viewHome", + component: () => import("@/pages/HomePage/ViewHome.vue"), + }, + ] + } + // { + // path: "home/:id", + // name: "home", + // component: () => import("@/pages/HomePage.vue"), + // children: [ + // ] + // } + ] + }, + { + path: "/import", + name: "import", + component: () => import("@/pages/ImportHomes.vue"), + }, + { + path: "/complex/:id", + name: "complex", + component: () => import("@/pages/ComplexPage.vue"), + }, + { + path: "/home/:id", + name: "home", + component: () => import("@/pages/HomePage.vue"), + children: [ + { + path: "main", + name: "homeMain", + component: () => import("@/pages/HomePage/MainPage.vue"), + }, + { + path: "chess", + name: "chess", + component: () => import("@/pages/HomePage/ChessPage.vue"), + }, + { + path: "view-home", + name: "viewHome", + component: () => import("@/pages/HomePage/ViewHome.vue"), + }, + { + path: "planLayout", + name: "planLayoutPage", + component: () => import("@/pages/HomePage/PlanLayoutPage.vue"), + }, + { + path: "floor-plan/:floor_plan_id", + name: "floors", + component: () => import("@/pages/EditPlanFloors.vue"), + }, + { + path: "facades", + name: "facades", + component: () => import("@/pages/EditFacades.vue"), + }, + ], + }, +] + +const router = createRouter({ + history: createWebHashHistory(), + + routes +}) + +export default router diff --git a/src/app/store/complex.js b/src/app/store/complex.js new file mode 100644 index 0000000..04c68bb --- /dev/null +++ b/src/app/store/complex.js @@ -0,0 +1,263 @@ +import { defineStore } from "pinia"; +import { onMounted, reactive, ref, watch } from "vue"; +import api from '@/shared/api' +import { useRoute, useRouter } from "vue-router"; +import { copyObject } from "@/shared/utils/util"; + +const INITIAL_FILTERS = { + selectedTypes: [], + + selectedMetros: [], + selectedRayons: [], + selectedComplexes: [], + selectedDecorations: [], + selectedComplexTypes: [], + address: '', + period: '', + area_from: '', + area_to: '', + rooms: [], + useSquarePrice: false, + rayons: [], + squareKitchen_min: '', + squareKitchen_max: '', + complexes: [], + // area_from: '', + // area_to: '', + floor_from: '', + floor_to: '', + price_from: '', + price_to: '', + // 'metros': [], + // 'period': 0, + // 'complexes': [], + // 'decorations': [], + // 'builders': [], + // 'types_room': [], + // 'types_complex': [], + // 'payment_types': [], + // 'decoration_types': [], + // 'sq_all_do': null, + // 'sq_all_ot': null, + // 'flat_floor_ot': null, + // 'flat_floor_do': null, + // 'stoim_ot': null, + // 'stoim_do': null, + // 'region_id': 0, + // 'country_id': 0, +} + +export const useComplexStore = defineStore("object", () => { + const route = useRoute() + const router = useRouter() + const agencyName = ref(null); + const agencyLogo = ref(null); + const agencyPhone = ref(null); + const activeType = ref("tile"); + const activePage = ref(1); + const totalRecords = ref(0); + const allPages = ref(0); + const load = ref(true); + const archiveLoad = ref(true); + const complexes = ref({}); + const loaders = reactive({ + list: true, + archivedList: true + }) + + const archivedComplexes = ref([]) + const archivedAllPages = ref(0) + const archivedTotalRecords = ref(0); + const activeComplex = ref(null) + + const rowsComplex = ref(10); + const types = ref({ + 'list': "Список", + 'tile': "Плитка", + // 'table': "Таблица", + }); + + const setActiveComplex = (complex) => { + activeComplex.value = complex + } + + const parseQueryFilters = () => { + const multipleKeys = ['selectedTypes', 'selectedComplexTypes', 'rooms', 'selectedMetros', 'complexes', 'selectedDecorations'] + + + Object.entries(route.query).forEach(([key, value]) => { + if (multipleKeys.includes(key)) { + if (Array.isArray(value)) { + value = value.map((num) => { + if (!isNaN(+num) && key != 'rooms') { + return parseInt(num) + } + + return num + }); + } + + if (typeof value == 'string' ) { + if (!isNaN(+value) && key != 'rooms') { + value = [parseInt(value)]; + } else { + value = [value]; + } + } + } + + if (key == 'rayons') { + if (Array.isArray(value)) { + value = value.map((num) => num); + } + + if (typeof value == 'string' ) { + value = [value]; + } + } + + complex_filter.value[key] = value == 'true' ? true : value == 'false' ? false : value + }) + + if (route.query.page) { + activePage.value = +route.query.page + } + } + + + onMounted(parseQueryFilters) + + const changeActiveType = (type) => { + activeType.value = type; + }; + + + + const clearFilters = () => { + router.replace({query: {}}); + complex_filter.value = copyObject(INITIAL_FILTERS) + } + + const complex_filter = ref(copyObject(INITIAL_FILTERS)); + + watch(activePage, (newValue) => { + + if (['main', 'complexes-list'].includes(route.name) && !loaders.list) { + getListComplex(); + } + if (route.name == 'archive-main' && !loaders.archivedList) { + getArchivedListComplex() + } + }) + + watch(() => route.name, (page, oldPage) => { + if (page == oldPage) return; + + if (page == 'archive-main') { + getArchivedListComplex() + } else if (page == 'main') { + getListComplex() + } + }, { deep: true }) + +async function getListComplex(withPage = true){ + loaders.list = true; + if (!withPage) activePage.value = 1 + api.searchComplexes( + { + ...complex_filter.value, + address: complex_filter.value.address.trim() + }, + activePage.value, + !route.meta.isViewPage + ) + .then((response) => { + + var res = response.data; + console.log('Response from backend:', res); + agencyName.value = res.agency_name; + agencyLogo.value = res.agency_logo; + agencyPhone.value = res.agency_phone; + complexes.value = res.list; + totalRecords.value = res.total; + allPages.value = res.allPages; + + const containers = document.getElementsByTagName('main'); + + if (containers) { + containers[0].scrollTo({ top: 0, behavior: 'smooth'}) + } + + }) + .catch(function(error) { + console.log(error); + }) + .finally(() => { + loaders.list = false + }); + console.log(complexes.value ,"res.listres.listres.listres.listres.listres.list"); +} + +async function getArchivedListComplex(withPage = true){ + loaders.archivedList = true + if (!withPage) activePage.value = 1 + api.searchComplexes( + { + ...complex_filter.value, + address: complex_filter.value.address.trim(), + is_archive: true + }, + activePage.value, + !route.meta.isViewPage + ) + .then((response) => { + var res = response.data; + agencyName.value = res.agency_name; + agencyLogo.value = res.agency_logo; + agencyPhone.value = res.agency_phone; + archivedComplexes.value = res.list; + archivedTotalRecords.value = res.total; + archivedAllPages.value = res.allPages; + + const containers = document.getElementsByTagName('main'); + + if (containers) { + containers[0].scrollTo({ top: 0, behavior: 'smooth'}) + } + + }) + .catch(function(error) { + console.log(error); + }) + .finally(() => { + loaders.archivedList = false + }); +} + + return { + activeType, + types, + changeActiveType, + activePage, + getListComplex, + getArchivedListComplex, + archivedAllPages, + archivedTotalRecords, + archivedComplexes, + rowsComplex, + allPages, + complex_filter, + complexes, + load, + loaders, + archivedListLoading: loaders.archivedList, + archiveLoad, + totalRecords, + activeComplex, + setActiveComplex, + clearFilters, + agencyName, + agencyLogo, + agencyPhone + }; +}); \ No newline at end of file diff --git a/src/app/store/fields.js b/src/app/store/fields.js new file mode 100644 index 0000000..5406ac6 --- /dev/null +++ b/src/app/store/fields.js @@ -0,0 +1,181 @@ +import { defineStore } from "pinia"; +import { onMounted, reactive, ref, watch } from "vue"; +import api from '@/shared/api' +import { useRoute } from "vue-router"; + +export const useFieldsStore = defineStore("fields", () => { + const route = useRoute() + const decorations = reactive([]) + const complexes_types = reactive([]) + const complex_suggestions = reactive([]) + const countries = reactive([]) + const metros = reactive([]) + const windowsPlacements = reactive([]) + const rf_regions = reactive([]) + const rayons = reactive([]) + const complex_status = reactive([]) + const currentRegionInfo = reactive({ + region_id: null, + region_id_user: null, + kladr: null, + label: null + }) + const regionsInfo = reactive({ + regions: [], + regions_list: {} + }) + setTimeout(() => { + const colors = JSON.parse(localStorage.getItem('colors')) + const resColor = colors.color_reserved + const freeColor = colors.color_free + + complex_status.value = [ + { + name: 'Свободные', + status: 'available', + color: freeColor, + }, + { + name: 'Забронированные', + status: 'reservation', + color: resColor, + }, + { + name: 'Проданные', + status: 'sold', + color: '#757575', + }, + { + name: 'Недоступные', + status: 'not_for_sale', + color: '#dedede', + }, + ] + + console.log(complex_status, 'СТАТУСЫ СПУСТЯ СЕКУНДУ') + }, 1000) + + const builders = reactive({ + list: [], + options: [] + }) + + // const fetchComplexStatuses = () => { + // api.getComplexStatus().then((response) => { + // if (response.data) { + // complex_status.push(...response.data) + // } + // }) + // } + + const refetchDecorationsList = () => { + api.getDecorations() + .then((response) => { + decorations.splice(0) + decorations.push(...response.data); + }) + } + + const fetchComplexSuggestions = (isViewPage) => api.getComplexSuggestions(isViewPage) + .then((response) => { + complex_suggestions.push(...response.data.list) + }) + + onMounted(() => { + api.getDecorations() + .then((response) => { + decorations.push(...response.data); + }) + + api.getComplexTypes() + .then((response) => { + complexes_types.push(...response.data) + }) + + // api.getCountry() + // .then((response) => { + // // console.log(response, 'responseresponseresponseresponseresponse'); + // + // countries.push(...response.data?.country) + // }) + + // api.getRFRegions() + // .then((response) => { + // const currentRegion = response.data.regions.find(region => region.code == response.data.region_id_user) + // + // if (currentRegion) { + // rayons.push(...currentRegion.rayons) + // } + // + // api.getMetro(response.data.region_id_user).then(metrosResponse => { + // metros.push(...metrosResponse.data) + // }) + // + // currentRegionInfo.region_id = parseInt(response.data.region_id_user) + // currentRegionInfo.region_id_user = parseInt(response.data.region_id_user); + // + // regionsInfo.regions.push(...response.data.regions) + // regionsInfo.regions_list = response.data.regions_list + // + // currentRegionInfo.kladr = regionsInfo.regions_list[currentRegionInfo.region_id].kladr + // currentRegionInfo.label = regionsInfo.regions_list[currentRegionInfo.region_id].label + // }) + + api.getWindowsPlacements() + .then((response) => { + windowsPlacements.push(...response.data) + }) + + // api.getBuilders() + // .then((response) => { + // builders.list = response.data.list + // builders.options.push(...response.data?.options) + // }) + + + fetchComplexSuggestions(!route.meta.isViewPage) + + // fetchComplexStatuses() + }) + + const changeRegionInfoRegionId = (payload) => { + currentRegionInfo.region_id = payload + + api.getMetro(currentRegionInfo.region_id) + .then((response) => { + metros.splice(0) + }) + } + + const metroListChanged = (region_id) => { + api.getMetro(region_id) + .then((response) => { + metros.splice(0) + metros.push(...response.data) + }) + } + + watch(() => route.meta.isViewPage, (newValue, prevValue) => { + if (newValue !== prevValue) { + fetchComplexSuggestions(!newValue) + } + }, { deep: true }) + + return { + decorations, + complexes_types, + countries, + rf_regions, + rayons, + metros, + currentRegionInfo, + regionsInfo, + windowsPlacements, + complex_suggestions, + changeRegionInfoRegionId, + metroListChanged, + builders, + complex_status, + refetchDecorationsList + }; +}); \ No newline at end of file diff --git a/src/app/store/house.js b/src/app/store/house.js new file mode 100644 index 0000000..4573482 --- /dev/null +++ b/src/app/store/house.js @@ -0,0 +1,28 @@ +import { defineStore } from "pinia"; +import { ref } from "vue"; +import api from '@/shared/api' + +export const useHouseStore = defineStore("house", () => { + const house = ref({}); + const complex = ref({'dop':{}, 'metro':{}}); + + const get_house = async (house_id, includes = []) => { + return api.getHouseOne(house_id, includes) + .then((response) => { + const res = response.data; + if(res.house) house.value = res.house; + if(res.complex) complex.value = res.complex; + + return response + }) + .catch((error) => { + console.log(error); + }); + } + + return { + house, + complex, + get_house + }; +}); \ No newline at end of file diff --git a/src/app/store/navigate.js b/src/app/store/navigate.js new file mode 100644 index 0000000..34ebef1 --- /dev/null +++ b/src/app/store/navigate.js @@ -0,0 +1,28 @@ +import { defineStore } from "pinia"; +import { ref } from "vue"; +import { useRoute } from "vue-router"; + +export const useNavigateStore = defineStore("navigate", () => { + const pages = ref([]); + const route = useRoute(); + + const setPages = (newPages) => { + const token = route.params.token; + + pages.value = newPages.map(page => { + // Найдём часть после `/view`, чтобы не дублировать + const viewIndex = page.path.indexOf('/view'); + const suffix = viewIndex !== -1 ? page.path.slice(viewIndex + 5) : ''; // +5 чтобы пропустить "/view" + + return { + ...page, + path: `/${token}/view${suffix}` + }; + }); + }; + + return { + pages, + setPages + }; +}); \ No newline at end of file diff --git a/src/app/styles/_mixins.scss b/src/app/styles/_mixins.scss new file mode 100644 index 0000000..fa1c337 --- /dev/null +++ b/src/app/styles/_mixins.scss @@ -0,0 +1,10 @@ +@mixin font-face($font-family, $url, $weight, $style) { + @font-face { + font-family: "#{$font-family}"; + src: url("@/shared/assets/fonts/#{$url}.woff") format("woff"), + url("@/shared/assets/fonts/#{$url}.woff2") format("woff2"); + font-weight: #{$weight}; + font-display: swap; + font-style: $style; + } +} diff --git a/src/app/styles/function.scss b/src/app/styles/function.scss new file mode 100644 index 0000000..1599816 --- /dev/null +++ b/src/app/styles/function.scss @@ -0,0 +1,9 @@ +@use "sass:math"; + +@function vw($value, $base: 1440) { + @return math.div($value, $base) * 100vw; +} + +@function vmin($value, $base: 320) { + @return math.div($value, $base) * 100vmin; +} diff --git a/src/app/styles/main.scss b/src/app/styles/main.scss new file mode 100644 index 0000000..3b94ab6 --- /dev/null +++ b/src/app/styles/main.scss @@ -0,0 +1,214 @@ +@import "./mixins"; + +@import "primevue/resources/themes/lara-light-indigo/theme.css"; +// @import url("https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700;900&display=swap"); +@tailwind base; +@tailwind components; +@tailwind utilities; +//@layer vendor, custom; + +:root { + --main-color: #ED4B00; + --reservation-color: #f2b720; + --available-color: #4caf50; +} + +.main-bg { + background-color: var(--main-color); +} + +@include font-face("Lato", "Lato-Black", 900, "normal"); +@include font-face("Lato", "Lato-ExtraBold", 800, "normal"); +@include font-face("Lato", "Lato-Bold", 700, "normal"); +@include font-face("Lato", "Lato-SemiBold", 600, "normal"); +@include font-face("Lato", "Lato-Medium", 500, "normal"); +@include font-face("Lato", "Lato-Regular", 400, "normal"); +@include font-face("Lato", "Lato-Light", 300, "normal"); +@include font-face("Lato", "Lato-ExtraLight", 200, "normal"); +@include font-face("Lato", "Lato-Thin", 100, "normal"); + +html , body { + width: 100%; + height: 100vh; + overflow: hidden; + overflow-x: hidden; +} + +.modelWindow { + z-index: 1000 !important; +} + + +img, video { + display: unset; + vertical-align: unset; +} + +.p-menuitem-link { + border: none !important; +} +.p-link:focus { + box-shadow: unset !important; +} + +.p-autocomplete input { + width: 100% !important; +} + +.p-autocomplete input:focus, .p-autocomplete input:hover { + box-shadow: unset !important; + border-color: rgb(76 175 80 / 1) !important;; +} + +.p-dropdown:not(.p-disabled):hover { + border-color: rgb(76 175 80 / 1); +} + +.p-dropdown:not(.p-disabled):focus { + border-color: rgb(76 175 80 / 1) !important; +} +.p-dropdown{ + padding: 2px; +} + +.p-dropdown:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem #C7D2FE; + border-color: rgb(76 175 80 / 1) !important; +} + +.loader-div{ + position: fixed; + width: 105vw ; + height: 100vh; + z-index: 2000000; + display: flex; + justify-content: center; + align-items: center; + top: 0; + left: 50%; + transform: translateX(-50%); + background-color: rgb(255, 255, 255); + svg{ + fill: #07a860 !important; + } +} + @media screen and (max-width :768px) { + + .loader-div{ + position: fixed; + width: 100vw ; + height: 100vh; + z-index: 2000000; + background-color: rgb(255, 255, 255); + display: flex; + justify-content: center; + align-items: center; + top: 0; + transform: translateX(150px); + } + } + +.custom-header { + display: flex !important; + + .p-checkbox-box, input { + box-shadow: unset !important; + } + + input { + padding: 5px !important; + } +} + +.loadingScreen { + width: 100%; + height: auto; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + + &__content { + width: 100%; + height: auto; + border-radius: 3px; + border: 1px solid #dedede; + background: var(--linear, + linear-gradient(180deg, #fefefe 0%, #f6f6f6 100%)); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 30px; + margin-bottom: 30px; + } + + &__title { + font-size: 21px; + font-weight: 500; + margin-bottom: 14px; + } + + &__text { + font-size: 12px; + text-align: center; + @apply text-grey-900; + } +} + +.p-paginator .p-paginator-pages .p-paginator-page.p-highlight { + color: #FFFFFF!important; + background: var(--main-color)!important; +} + +.p-dropdown-panel { + max-width: 435px; +} +.p-dropdown-panel .p-dropdown-items { + padding: 0!important; +} + +.p-dropdown-items-wrapper { + overflow: hidden; +} + +.primary-btn { + background-color: var(--main-color); + width: 207px; + height: 37px; + display: flex; + justify-content: center; + align-items: center; + color: white; + border-radius: 20px; + cursor: pointer; + border: 1px solid #DDDDDD; +} + +/* Уменьшаем ширину скроллбара */ +::-webkit-scrollbar { + width: 4px; /* Тонкий скроллбар */ + height: 4px; +} + +/* Делаем трек (фон под скроллом) */ +::-webkit-scrollbar-track { + background: #f0f0f0; /* Цвет фона скроллбара */ + // border-radius: 10px; +} + +/* Сама "ручка" (ползунок) */ +::-webkit-scrollbar-thumb { + background: var(--main-color); /* Цвет ползунка */ + // border-radius: 10px; /* Закругляем края */ +} + +/* При наведении делаем ползунок темнее */ +::-webkit-scrollbar-thumb:hover { + background: var(--main-color); +} +// html { +// overflow-y: scroll; /* Всегда оставляет место для скролла */ +// } \ No newline at end of file diff --git a/src/app/types/complex.ts b/src/app/types/complex.ts new file mode 100644 index 0000000..d8273c9 --- /dev/null +++ b/src/app/types/complex.ts @@ -0,0 +1,61 @@ +export type TTypeGrid = Record; + +export type TStatus = Record; + +export type TTypeObject = Record; + +export type TCountry = Record; + +export type TSubject = Record; + +export type TMetro = Record; + +export type TTypeToMetro = Record; + +export type TVideo = Record; + +export type TUniqueOffer = { + title: string; + description: string; +}; + +export type TComplex = { + _id?: string; + idDomClick: string; + type: TTypeObject; + country: TCountry; + region: string; + adress: string; + metro: TMetro; + timeToMetro: { + time: number; + typeToMetro: TTypeToMetro; + }; + title: string; + mainDescription: { + title: string; + text: string; + }; + secondaryDescription?: { + title: string; + text: string; + }; + video?: string; + typeVideo?: TVideo; + uniqueOffer?: TUniqueOffer[] | string[]; + image: string; + stocks?: string; + queue?: string; +}; + +export type TComplexNew = { + _id?: string; + type: string; + idDomClick: string; + country: string; + region: string; + address: string; + metro: string; + title: string; + uniqueOffer: TUniqueOffer[]; +}; diff --git a/src/app/types/facadeCanvas.js b/src/app/types/facadeCanvas.js new file mode 100644 index 0000000..0bae738 --- /dev/null +++ b/src/app/types/facadeCanvas.js @@ -0,0 +1,88 @@ +import Collection from "ol/Collection"; +import Feature from "ol/Feature"; +import Map from "ol/Map"; +import Overlay from "ol/Overlay"; +import View from "ol/View"; +import { Coordinate } from "ol/coordinate"; +import { Polygon } from "ol/geom"; +import Draw from "ol/interaction/Draw"; +import Modify from "ol/interaction/Modify"; +import Select from "ol/interaction/Select"; +import Snap from "ol/interaction/Snap"; +import ImageLayer from "ol/layer/Image"; +import VectorLayer from "ol/layer/Vector"; +import { Projection } from "ol/proj"; +import Static from "ol/source/ImageStatic"; +import VectorSource from "ol/source/Vector"; +import Style from "ol/style/Style"; + +/*TDraw = { + interaction: Draw; + sketchFeature_: null | Feature; + setActive: (active: boolean) => void; + coordToString: (coordXY: Coordinate) => string; + isOnePoint: (coords: Coordinate[]) => boolean; + getFeature: () => Feature; + isEmpty: () => boolean; +}; + +export type TSelect = { + interaction: Select; + setEvents: () => void; + setActive: (active: boolean) => void; + clear: () => void; +}; + +export type TModify = { + interaction: Modify; + setActive: (active: boolean) => void; +}; + +export type Facades = { + id: string; + floor_position: string; + points: Coordinate[]; +}; + +export type TOlMap = { + map: Map; + features: Collection>; + sourceVector: VectorSource; + draw: TDraw; + modify: TModify; + select: TSelect; + snap: Snap; + imageElement: HTMLImageElement; + imageLayer: ImageLayer; + target: string; + vector: VectorLayer; + overlay: Overlay; + color: string; + opacity: number; + hovered: boolean; + selectFeature: Feature; + onImageLoad: () => void; + createMap: (imageElement: HTMLImageElement, target: string) => void; + addInteractionsToMap: (map: Map, vector: VectorLayer) => void; + setColor: (color: string, opacity: number) => void; + hexToRgba: (hex: string, opacity?: number) => string; + addEvents: () => void; + removeStyleFromFeatures: (currentFeature?: Feature) => void; + createVector: () => VectorLayer; + createSourceVector: () => VectorSource; + createStyle: () => Style; + createView: ( + imageElement: HTMLImageElement | HTMLElement, + target?: HTMLElement + ) => View; + createProjection: ( + imageElement: HTMLImageElement | HTMLElement, + target?: HTMLElement + ) => Projection; + createImageLayer: ( + imageElement: HTMLImageElement, + view?: View + ) => ImageLayer; + createImageStatic: (imageElement: HTMLImageElement, view?: View) => Static; + createOverlay: (container: HTMLElement) => Overlay; +};*/ diff --git a/src/app/types/facadeViewCanvas.js b/src/app/types/facadeViewCanvas.js new file mode 100644 index 0000000..da9e734 --- /dev/null +++ b/src/app/types/facadeViewCanvas.js @@ -0,0 +1,84 @@ +import View from "ol/View"; +import Collection from "ol/Collection"; +import Map from "ol/Map"; +import ImageLayer from "ol/layer/Image"; +import VectorLayer from "ol/layer/Vector"; +import { Projection } from "ol/proj"; +import Static from "ol/source/ImageStatic"; +import VectorSource from "ol/source/Vector"; +import Feature from "ol/Feature"; +import { Polygon } from "ol/geom"; +import Style from "ol/style/Style"; +import Overlay from "ol/Overlay"; +import Modify from "ol/interaction/Modify"; +import Select from "ol/interaction/Select"; +import Draw from "ol/interaction/Draw"; +import { Coordinate } from "ol/coordinate"; + +/*export type TOlMap = { + map: Map; + target: string; + imageElement: HTMLImageElement; + imageLayer: ImageLayer; + vector: VectorLayer; + sourceVector: VectorSource; + features: Collection>; + overlay: Overlay; + opacity: number; + color: string; + modify: TModify; + select: TSelect; + hovered: boolean; + selectFeature: Feature; + onImageLoad: () => void; + createMap: (imageElement: HTMLImageElement, target: string) => void; + createImageLayer: (imageElement: HTMLImageElement) => ImageLayer; + createImageStatic: (imageElement: HTMLImageElement) => Static; + createView: ( + imageElement: HTMLImageElement, + targetElement: HTMLElement + ) => View; + createProjection: (imageElement: HTMLImageElement) => Projection; + createVector: () => VectorLayer; + createSourceVector: () => VectorSource; + createStyle: () => Style; + createOverlay: (container: HTMLElement) => Overlay; + addInteractionsToMap: (map: Map, vector: VectorLayer) => void; + addEvents: () => void; + hexToRgba: (color: string, opacity?: number) => string; + removeStyleFromFeatures: (currentFeature?: Feature) => void; + resize: () => void; + setColor: (color: string, opacity?: number) => void; +}; + +export type TModify = { + interaction: Modify; + setActive: (active: boolean) => void; +}; + +export type TSelect = { + interaction: Select; + setEvents: () => void; + setActive: (active: boolean) => void; + clear: () => void; +}; + +export type TDraw = { + interaction: Draw; + setActive: (active: boolean) => void; + coordToString: (coordXY: Coordinate) => string; + isOnePoint: (coords: Coordinate[]) => boolean; + getFeature: () => Feature; +}; + +export type Area = { + id: string; + points: Coordinate[]; + section?: string; + floor?: string; +}; + +export interface MyFeature extends Feature { + floor: string; + section: string; +}*/ diff --git a/src/app/types/facades.js b/src/app/types/facades.js new file mode 100644 index 0000000..ff24f9b --- /dev/null +++ b/src/app/types/facades.js @@ -0,0 +1,34 @@ +import { Coordinate } from "ol/coordinate"; + +/*export interface IFacade { + readonly id: string; + image: string | HTMLImageElement; + title: string; + order: number; + isPublish: boolean; + floors: FloorWrapper; +} + +export type FloorWrapper = { + countFloors: number; + items: Floor[]; +}; + +export type Floor = { + readonly id: string; + points: Point[]; + numberFloor: number; + section: number; +}; + +export type Point = { + x: number; + y: number; +};*/ + +/*type['Area'] = { + id, + points: [], + section: String, + floor: String, +};*/ diff --git a/src/components/BurgerIcon.vue b/src/components/BurgerIcon.vue new file mode 100644 index 0000000..18ad026 --- /dev/null +++ b/src/components/BurgerIcon.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/components/CanvasNext.vue b/src/components/CanvasNext.vue new file mode 100644 index 0000000..c2782b0 --- /dev/null +++ b/src/components/CanvasNext.vue @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/src/components/CanvasPrev.vue b/src/components/CanvasPrev.vue new file mode 100644 index 0000000..19e3745 --- /dev/null +++ b/src/components/CanvasPrev.vue @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue new file mode 100644 index 0000000..b9f3c7d --- /dev/null +++ b/src/components/HelloWorld.vue @@ -0,0 +1,59 @@ + + + + + + diff --git a/src/components/IframeApartmentType.vue b/src/components/IframeApartmentType.vue new file mode 100644 index 0000000..8aa5cf1 --- /dev/null +++ b/src/components/IframeApartmentType.vue @@ -0,0 +1,249 @@ + + + + + \ No newline at end of file diff --git a/src/components/IframeNavbar.vue b/src/components/IframeNavbar.vue new file mode 100644 index 0000000..980bd7c --- /dev/null +++ b/src/components/IframeNavbar.vue @@ -0,0 +1,177 @@ + + + + + + \ No newline at end of file diff --git a/src/components/IframeSearchForm.vue b/src/components/IframeSearchForm.vue new file mode 100644 index 0000000..a65b73e --- /dev/null +++ b/src/components/IframeSearchForm.vue @@ -0,0 +1,469 @@ + + + + + + \ No newline at end of file diff --git a/src/components/Loader.vue b/src/components/Loader.vue new file mode 100644 index 0000000..d932b4d --- /dev/null +++ b/src/components/Loader.vue @@ -0,0 +1,35 @@ + + + + \ No newline at end of file diff --git a/src/components/MapIcon.vue b/src/components/MapIcon.vue new file mode 100644 index 0000000..c9c45d8 --- /dev/null +++ b/src/components/MapIcon.vue @@ -0,0 +1,47 @@ + + + \ No newline at end of file diff --git a/src/components/Navigation.vue b/src/components/Navigation.vue new file mode 100644 index 0000000..698a073 --- /dev/null +++ b/src/components/Navigation.vue @@ -0,0 +1,96 @@ + + + + + + diff --git a/src/components/NoResults.vue b/src/components/NoResults.vue new file mode 100644 index 0000000..e5a4187 --- /dev/null +++ b/src/components/NoResults.vue @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/src/components/NoResultsForDeveloper.vue b/src/components/NoResultsForDeveloper.vue new file mode 100644 index 0000000..7e3aed2 --- /dev/null +++ b/src/components/NoResultsForDeveloper.vue @@ -0,0 +1,38 @@ + + + + \ No newline at end of file diff --git a/src/components/RocketSpinner.vue b/src/components/RocketSpinner.vue new file mode 100644 index 0000000..360d00b --- /dev/null +++ b/src/components/RocketSpinner.vue @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/src/components/SearchForm.vue b/src/components/SearchForm.vue new file mode 100644 index 0000000..faaa097 --- /dev/null +++ b/src/components/SearchForm.vue @@ -0,0 +1,469 @@ + + + + + + \ No newline at end of file diff --git a/src/components/Tabs.vue b/src/components/Tabs.vue new file mode 100644 index 0000000..4d53563 --- /dev/null +++ b/src/components/Tabs.vue @@ -0,0 +1,122 @@ + + + + + + diff --git a/src/components/YandexMaps.vue b/src/components/YandexMaps.vue new file mode 100644 index 0000000..ff6149a --- /dev/null +++ b/src/components/YandexMaps.vue @@ -0,0 +1,117 @@ + + + + \ No newline at end of file diff --git a/src/entities/changeSideWorld.vue b/src/entities/changeSideWorld.vue new file mode 100644 index 0000000..0c52607 --- /dev/null +++ b/src/entities/changeSideWorld.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/src/entities/complexItem.vue b/src/entities/complexItem.vue new file mode 100644 index 0000000..352daef --- /dev/null +++ b/src/entities/complexItem.vue @@ -0,0 +1,751 @@ + + + + + + + diff --git a/src/entities/complexItemIframe.vue b/src/entities/complexItemIframe.vue new file mode 100644 index 0000000..c22525b --- /dev/null +++ b/src/entities/complexItemIframe.vue @@ -0,0 +1,424 @@ + + + + + + + \ No newline at end of file diff --git a/src/entities/homeItem.vue b/src/entities/homeItem.vue new file mode 100644 index 0000000..a338aa4 --- /dev/null +++ b/src/entities/homeItem.vue @@ -0,0 +1,242 @@ + + + + + + + diff --git a/src/entities/homeItemIframe.vue b/src/entities/homeItemIframe.vue new file mode 100644 index 0000000..4f5d02a --- /dev/null +++ b/src/entities/homeItemIframe.vue @@ -0,0 +1,447 @@ + + + + + + + \ No newline at end of file diff --git a/src/entities/tableEditeble.vue b/src/entities/tableEditeble.vue new file mode 100644 index 0000000..1ae3fe1 --- /dev/null +++ b/src/entities/tableEditeble.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/entities/tableFromFile.vue b/src/entities/tableFromFile.vue new file mode 100644 index 0000000..1f632d7 --- /dev/null +++ b/src/entities/tableFromFile.vue @@ -0,0 +1,107 @@ + + + + + + diff --git a/src/entities/viewHome/chessLayoutsItem.vue b/src/entities/viewHome/chessLayoutsItem.vue new file mode 100644 index 0000000..0f53f44 --- /dev/null +++ b/src/entities/viewHome/chessLayoutsItem.vue @@ -0,0 +1,489 @@ + + + + + \ No newline at end of file diff --git a/src/eventBus.js b/src/eventBus.js new file mode 100644 index 0000000..070512c --- /dev/null +++ b/src/eventBus.js @@ -0,0 +1,9 @@ +// eventBus.js +import emitter from 'tiny-emitter/instance' + +export default { + $on: (...args) => emitter.on(...args), + $once: (...args) => emitter.once(...args), + $off: (...args) => emitter.off(...args), + $emit: (...args) => emitter.emit(...args) +} \ No newline at end of file diff --git a/src/features/useEditFacades.js b/src/features/useEditFacades.js new file mode 100644 index 0000000..62c8710 --- /dev/null +++ b/src/features/useEditFacades.js @@ -0,0 +1,679 @@ +import Map from "ol/Map"; +import ZoomSlider from "ol/control/ZoomSlider"; +import Zoom from "ol/control/Zoom"; +import VectorLayer from "ol/layer/Vector"; +import VectorSource from "ol/source/Vector"; +import Collection from "ol/Collection"; +import Style from "ol/style/Style"; +import Fill from "ol/style/Fill"; +import Stroke from "ol/style/Stroke"; +import Circle from "ol/style/Circle"; +import View from "ol/View"; +import ImageLayer from "ol/layer/Image"; +import Static from "ol/source/ImageStatic"; +import { getCenter } from "ol/extent"; +import { Projection } from "ol/proj"; +import Overlay from "ol/Overlay"; +import OlModify from "ol/interaction/Modify"; +import { shiftKeyOnly, singleClick } from "ol/events/condition"; +import OlSelect from "ol/interaction/Select"; +import OlDraw, { DrawEvent } from "ol/interaction/Draw"; +import { Coordinate, toStringXY } from "ol/coordinate"; +import OlSnap from "ol/interaction/Snap"; +import { Polygon } from "ol/geom"; +import Feature, { FeatureLike } from "ol/Feature"; +import { Pixel } from "ol/pixel"; +import { + Facades, + TDraw, + TModify, + TOlMap, + TSelect, +} from "@/app/types/facadeCanvas"; +import { ref } from "vue"; +import { Area } from "@/app/types/facades"; + +export default function createCanvas( + facade, + onFigureAdded, + onOverlayToggled, +) { + const olMap = ref(); + const sourceVctr = ref(null) + const start = (modalEl) => { + olMap.value = new OlMap(facade.image, 'map', function () { + onMapLoad(); + }); + setMapHeight(modalEl); + }; + + const setMapHeight = function (modalEl) { + const map = document.querySelector("#map"); + const modalDialog = modalEl.querySelector(".modal-dialog"); + + const scrollTop = + window.pageYOffset || + document.documentElement.scrollTop || + document.body.scrollTop; + + const marginY = + parseInt(modalDialog.style.marginTop) + + parseInt(modalDialog.style.marginBottom); + + map.style.height = + window.innerHeight - + (map.getBoundingClientRect().top - scrollTop) - + marginY + + "px"; + }; + + const colorInit = () => { + olMap.value.setColor(facade.hover_color, 0.25); + }; + + function OlMap(imageUrl, target, onMapLoad) { + this.target = target; + this.imageElement = new Image(); + + this.imageElement.onload = () => { + this.onImageLoad(); + onMapLoad(); + }; + + this.imageElement.src = imageUrl; + } + + OlMap.prototype.onImageLoad = function () { + this.createMap(this.imageElement, this.target); + }; + + OlMap.prototype.createMap = function ( + imageElement, + target + ) { + this.imageLayer = this.createImageLayer(imageElement); + + const targetElement = document.getElementById(target); + const view = this.createView(imageElement); + + this.vector = this.createVector(); + this.overlay = this.createOverlay(document.getElementById("popup")); + + const paneNode = document.createElement("div"); + paneNode.className = "ol-map__pane"; + + targetElement.appendChild(paneNode); + + this.map = new Map({ + layers: [this.imageLayer, this.vector], + overlays: [this.overlay], + controls: [], + target: paneNode, + view: view, + pixelRatio: 1, + }); + + const zoomNode = document.createElement("div"); + zoomNode.className = "ol-map__zoom"; + + const zoomSlider = new ZoomSlider({ + className: "ol-map__zoom-slider facade-zoom-slider", + }); + + targetElement.prepend(zoomNode); + + zoomSlider.setTarget(zoomNode); + this.map.addControl(zoomSlider); + + const ctrlButtons = new Zoom({ + target: zoomNode, + className: "ol-map__zoom-btns", + zoomInLabel: "+", + zoomOutLabel: "\u2212", + delta: 1, + }); + + const zoomLabelNode = document.createElement("div"); + zoomLabelNode.className = "ol-map__zoom-label"; + zoomLabelNode.textContent = "масштаб"; + + zoomNode.appendChild(zoomLabelNode); + this.map.addControl(ctrlButtons); + this.addInteractionsToMap(this.map, this.vector); + this.addEvents(); + }; + + OlMap.prototype.addInteractionsToMap = function ( + map, + vector + ) { + this.modify = new Modify(map); + this.modify.setActive(true); + + this.select = new Select(map); + this.select.setActive(true); + + this.draw = new Draw(map, vector); + this.draw.setActive(true); + + this.snap = new Snap(map, vector); + }; + + OlMap.prototype.setColor = function ( + color, + opacity + ) { + this.color = color; + this.opacity = opacity || 20; + + const style = this.vector?.getStyle(); + if (style) style.getFill().setColor(this.hexToRgba(this.color, this.opacity)); + }; + + OlMap.prototype.hexToRgba = function ( + hex, + opacity + ) { + if (!hex) return; + hex = hex.replace("#", ""); + + const color = { + r: parseInt(hex.substring(0, 2), 16), + g: parseInt(hex.substring(2, 4), 16), + b: parseInt(hex.substring(4, 6), 16), + }; + + if (opacity) { + opacity = opacity > 1 ? opacity / 100 : opacity; + return ( + "rgba(" + + color.r + + ", " + + color.g + + ", " + + color.b + + ", " + + opacity + + ")" + ); + } else { + return "rgba(" + color.r + ", " + color.g + ", " + color.b + ")"; + } + }; + + /* Events */ + + OlMap.prototype.addEvents = function () { + this.map.on("click", function () { + //клик в области карты + }); + + this.map.on("pointermove", function (event) { + const opacity = olMap.value.opacity === 1 ? 0.99 : olMap.value.opacity; + + const hoverStyle = new Style({ + fill: new Fill({ + color: olMap.value.hexToRgba(olMap.value.color, 1 - opacity), + }), + stroke: new Stroke({ + color: "#0000000", + width: 0.01, + }), + image: new Circle({ + radius: 7, + fill: new Fill({ + color: olMap.value.hexToRgba(olMap.value.color), + }), + }), + }); + + let currentFeature; + + this.forEachFeatureAtPixel( + event.pixel, + function (feature) { + if (feature.getGeometry().getType() == "Polygon") { + currentFeature = feature; + } + } + ); + + if ( + currentFeature && + olMap.value.features.getArray().indexOf(currentFeature) > -1 + ) { + olMap.value.hovered = true; + currentFeature.setStyle(hoverStyle); + olMap.value.removeStyleFromFeatures(currentFeature); + } else if (olMap.value.hovered) { + olMap.value.hovered = false; + olMap.value.removeStyleFromFeatures(); + } + }); + }; + + OlMap.prototype.removeStyleFromFeatures = function ( + currentFeature + ) { + this.sourceVector?.getFeatures().forEach((feature) => { + if (currentFeature) { + if (feature.getId() !== currentFeature.getId()) { + feature.setStyle(olMap.value.createStyle()); + } + } else { + feature.setStyle(olMap.value.createStyle()); + } + }); + }; + + /* Vector, Style */ + + OlMap.prototype.createVector = function (){ + this.sourceVector = this.createSourceVector(); + sourceVctr.value = this.sourceVector + + const style = this.createStyle(); + + return new VectorLayer({ + source: this.sourceVector, + style: style, + }); + }; + + OlMap.prototype.createSourceVector = function () { + this.features = new Collection(); + + return new VectorSource({ + features: this.features, +}); + }; + + OlMap.prototype.createStyle = function () { + return new Style({ + fill: new Fill({ + color: olMap.value.hexToRgba(olMap.value.color, 0.25), + }), + stroke: new Stroke({ + color: "#ffcc33", + width: 2, + }), + image: new Circle({ + radius: 7, + fill: new Fill({ + color: "#ffcc33", + }), + }), + }); + }; + + /* View, Layer, Image */ + + OlMap.prototype.createView = function ( + imageElement + ) { + const projection = this.createProjection(imageElement); + + return new View({ + projection: projection, + center: getCenter([0, 0, imageElement.width, imageElement.height]), + zoom: 2, + minZoom: 1, + maxZoom: 6, + }); + }; + + OlMap.prototype.createProjection = function ( + imageElement + ) { + return new Projection({ + code: "olimage", + units: "pixels", + extent: [0, 0, imageElement.width, imageElement.height], + }); + }; + + OlMap.prototype.createImageLayer = function ( + imageElement + ) { + const imageStatic = this.createImageStatic(imageElement); + + return new ImageLayer({ + source: imageStatic, + }); + }; + OlMap.prototype.createImageStatic = function ( + imageElement + ) { + return new Static({ + url: imageElement.src, + imageExtent: [0, 0, imageElement.width, imageElement.height], + }); + }; + + OlMap.prototype.createOverlay = function ( + container + ) { + return new Overlay({ + element: container, + autoPan: { + animation: { + duration: 0, + }, + }, + }); + }; + + /* Interactions */ + + function Modify(map) { + this.interaction = new OlModify({ + features: olMap.value ? olMap.value.features : new Collection(), + deleteCondition: function (event) { + return shiftKeyOnly(event) && singleClick(event); + }, + }); + + map.addInteraction(this.interaction); + } + + Modify.prototype.setActive = function (active) { + this.interaction.setActive(active); + }; + + function Select(map) { + this.interaction = new OlSelect(); + map.addInteraction(this.interaction); + this.setEvents(); + } + + Select.prototype.setEvents = function () { + this.interaction.on( + "change:active", + function () { + this.clear(); + }.bind(this) + ); + }; + + Select.prototype.setActive = function (active) { + this.interaction.setActive(active); + }; + + Select.prototype.clear = function () { + const selectedFeatures = this.interaction.getFeatures(); + + selectedFeatures.forEach(() => selectedFeatures.remove(this.interaction.getFeatures()[0]) + ); + }; + + /** + * type TDraw + */ + + function Draw(map, vector) { + this.interaction = new OlDraw({ + source: vector.getSource(), + type: /** @type {ol.geom.GeometryType} */ "Polygon", + freehand: false, + condition: function (event) { + return event.originalEvent.which == 1; + }, + }); + + this.interaction.setActive(false); + map.addInteraction(this.interaction); + } + + Draw.prototype.coordToString = function (coordXY) { + return toStringXY(coordXY, 3); + }; + + Draw.prototype.isOnePoint = function ( + coords + ) { + return coords.every( + function (coordXY) { + return this.coordToString(coords[0]) == this.coordToString(coordXY); + }.bind(this) + ); + }; + + Draw.prototype.isEmpty = function () { + if (!this.getFeature()) { + return true; + } + + const coords = this.getFeature().getGeometry().getCoordinates()[0]; + + if (coords.length == 0 || this.isOnePoint(coords)) { + return true; + } + + return false; + }; + + Draw.prototype.getFeature = function () { + return this.sketchFeature_; + }; + + Draw.prototype.getActive = function () { + return this.interaction && this.interaction.getActive(); + }; + + Draw.prototype.setActive = function (active) { + this.interaction.setActive(active); + }; + + function Snap(map, vector) { + const snap = new OlSnap({ + source: vector.getSource(), + }); + + map.addInteraction(snap); + } + + function onMapLoad() { + initFeatures(); + + addMapEvents(); + + colorInit(); + } + + function initFeatures() { + (facade.figures || []).forEach(function (item) { + const feature = new Feature({ + geometry: new Polygon([item.points]), + }); + + feature.setId(item.id); + feature.set("enrance_id", item.enrance_id); + feature.set("floor_id", item.floor_id); + + try { + olMap.value.sourceVector.addFeature(feature); + } catch (e) { + location.reload(); + } + }); + } + + function addMapEvents() { + if (!olMap.value) return; + olMap.value.map + .getViewport() + .addEventListener("contextmenu", function (event) { + event.preventDefault(); + + rightClickHandler(event); + }); + + olMap.value.draw.interaction.on("change", function () { + // + }); + + olMap.value.draw.interaction.on("drawstart", function (event) { + olMap.value.draw.sketchFeature_ = event.target.sketchFeature_; + }); + + olMap.value.draw.interaction.on("drawend", function (event) { + olMap.value.draw.sketchFeature_ = event.target.sketchFeature_; + const id = Math.random().toString(16).slice(2); + + const feature = event.feature; + // event.feature.setId(id); + addFacade(feature); + }); + + // olMap.value.modify.interaction.on("modifyend", function (event) { + // console.log("modifyend"); + + // const calls = []; + // event.features.forEach(function (feature) { + // calls.push(editFacade(feature)); + // }); + // }); + + olMap.value.map.on("dblclick", function (e) { + if (olMap.value.draw.interaction.getPointerCount()) { + olMap.value.draw.interaction.removeLastPoint(); + olMap.value.draw.interaction.removeLastPoint(); + } + + showOverlayAtPixel(olMap.value.map.getEventPixel(e.originalEvent)); + }); + } + + function rightClickHandler(event) { + if (!olMap.value.draw.isEmpty()) { + olMap.value.draw.interaction.removeLastPoint(); + return; + } + + if (!olMap.value.draw.isEmpty()) { + return; + } + + showOverlayAtPixel(olMap.value.map.getEventPixel(event)); + } + + function showOverlayAtPixel(pixel) { + const feature = getFeatureAtPixel(pixel); + + if (!feature) { + return; + } + + showOverLay(feature); + } + + function getFeatureAtPixel(pixel) { + let polygonFeature; + + olMap.value.map.forEachFeatureAtPixel(pixel, function (feature) { + if ( + feature.getGeometry().getType() == "Polygon" && + olMap.value.features.getArray().indexOf(feature) > + -1 + ) { + polygonFeature = feature; + } + }); + + return polygonFeature; + } + + // function editFacade(feature) { + // const geom = feature.getGeometry(); + + // console.log(facade, 'editFacadettt'); + + + // facade.figures.find( + // (figure) => figure.id == feature.getId() + // ).points = geom.getCoordinates()[0]; + + // showOverLay(feature); + // } + + function addFacade(feature) { + const geom = feature.getGeometry(); + + const data = { + id: String(feature.getId()), + points: geom.getCoordinates()[0], + }; + + onFigureAdded(geom.getCoordinates()[0]).then((figure) => { + if (figure) { + feature.setId(figure.id) + showOverLay(feature); + } + }) + + } + + function saveOverlay(data) { + closeOverlay(); + } + + function showOverLay(feature) { + onOverlayToggled(feature.getId()) + olMap.value.select.clear(); + + olMap.value.select.interaction.getFeatures().push(feature); + olMap.value.selectFeature = feature; + + const coordinates = feature.getGeometry().getCoordinates()[0]; + let currentCoordinate = coordinates[0]; + + coordinates.forEach(function (coord) { + if (coord[0] > currentCoordinate[0] && coord[1] > currentCoordinate[1]) { + currentCoordinate = coord; + } + }); + + olMap.value.overlay.setPosition(currentCoordinate); + + addOverlayEvents(feature); + } + + function addOverlayEvents(feature) { + const popup = document.querySelector("#popup"); + popup.addEventListener("mousedown", (e) => { + e.stopPropagation(); + e.preventDefault(); + }); + + document.getElementById("popup-closer").addEventListener("click", function () { + closeOverlay(); + }); + + } + + function closeOverlay() { + olMap.value.overlay.setPosition(undefined); + const popupClose= document + .querySelector("#popup") + .querySelector("#popup-closer"); + popupClose.blur(); + olMap.value.selectFeature = null; + return false; + } + + function deleteSelectedFeature(feature_id) { + const feature = sourceVctr.value.getFeatures().find((feature) => feature.getId() == feature_id) + if (feature) { + olMap.value.sourceVector.removeFeature(feature); + closeOverlay(); + } + } + + return { + start, + olMap, + saveOverlay, + showOverLay, + deleteSelectedFeature, + }; +} diff --git a/src/features/useEntranceHooks.js b/src/features/useEntranceHooks.js new file mode 100644 index 0000000..5e4bc44 --- /dev/null +++ b/src/features/useEntranceHooks.js @@ -0,0 +1,88 @@ +import { computed, ref, watch } from "vue"; +import api from "@/shared/api"; + +export default function useEntranceHooks() { + const entranceList = ref([]); + const entrancesLoader = ref(true) + + const getEntranceList = (house_id) => { + return api.getHouseEntrances(house_id, ['rooms']) + .then((response) => { + entranceList.value = response.data + }) + .finally(() => entrancesLoader.value = false) + .catch(function(error) { + console.log(error); + }); + } + + const updateEntrance = async (entrance, fields) => { + api.updateEntrance(entrance.id, fields) + .then((response) => { + entrance = {...entrance , ...fields} + }) + .catch(function(error) { + console.log(error); + }); + } + + const deleteEntrance = async (entrance_id) => { + api.deleteEntrance(entrance_id) + .then(() => { + entranceList.value = entranceList.value.filter(entrance => entrance.id !== entrance_id) + }) + .catch(function(error) { + console.log(error); + }); + } + + const addNewEntrance = (house_id, fields = {}) => { + fields.position = entranceList.value.length + 1; + fields.name = `Подъезд ${entranceList.value.length + 1}`; + + return api.createEntrance(house_id, fields) + .then((response) => { + entranceList.value.push(response.data) + return response.data + }) + .catch(function(error) { + console.log(error); + }); + }; + + const sortedEntranceList = computed(() => { + return entranceList.value.sort((a, b) => { + if (a.position > b.position) { + return 1; + } else { + return -1; + } + }); + }); + + const updateTitle = (index, name) => { + const entrance = entranceList.value.find((el) => el.id === index); + if (entrance) { + updateEntrance(entrance, {name}).then(() => { + entrance.name = name; + }) + } + }; + + watch(sortedEntranceList, (value) => { + value.forEach((entrance, index) => { + updateEntrance(entrance, { position: index + 1 }) + }) + }); + + return { + entranceList, + addNewEntrance, + sortedEntranceList, + updateEntrance, + updateTitle, + getEntranceList, + deleteEntrance, + entrancesLoader + }; +} diff --git a/src/features/useFloorHook.js b/src/features/useFloorHook.js new file mode 100644 index 0000000..43d52af --- /dev/null +++ b/src/features/useFloorHook.js @@ -0,0 +1,34 @@ +import api from "@/shared/api"; +import { computed, watch } from "vue"; + +export default function useFloorHooks(floors) { + const updateFloor = (floor, fields) => { + return api.updateFloor(floor.id, fields) + .then((response) => { + floor = {...floor , ...fields} + }) + .catch((error) => { + console.log(error); + }); + } + + const sortedFloors = computed(() => { + return floors ? floors.sort((a, b) => { + if (a.position > b.position) { + return 1; + } else { + return -1; + } + }) : []; + }); + + watch(sortedFloors, (value) => { + value.forEach((floor, index) => { + updateFloor(floor, { position: index + 1 }) + }) + }); + return { + sortedFloors, + updateFloor, + }; +} diff --git a/src/features/useHouseHook.js b/src/features/useHouseHook.js new file mode 100644 index 0000000..b91d3eb --- /dev/null +++ b/src/features/useHouseHook.js @@ -0,0 +1,26 @@ +import api from "@/shared/api"; +import { ref } from "vue"; + +export default function useHouseHook() { + const house = ref({}); + const complex = ref({'dop':{}, 'metro':{}}); + + const get_house = async (house_id) => { + api.getHouseOne(house_id) + .then((response) => { + var res = response.data; + if(res.house) house.value = res.house; + if(res.complex) complex.value = res.complex; + }) + .catch((error) => { + console.log(error); + }); + +} + + return { + house, + complex, + get_house + }; +} diff --git a/src/features/useViewFacades.js b/src/features/useViewFacades.js new file mode 100644 index 0000000..6582c1b --- /dev/null +++ b/src/features/useViewFacades.js @@ -0,0 +1,578 @@ +import Map from "ol/Map"; +import View from "ol/View"; +import ImageLayer from "ol/layer/Image"; +import Projection from "ol/proj/Projection"; +import { defaults as InteractionDefault } from "ol/interaction/defaults"; +import { defaults as ControlDefault } from "ol/control/defaults"; +import Static from "ol/source/ImageStatic"; +import { getCenter } from "ol/extent"; +import VectorLayer from "ol/layer/Vector"; +import VectorSource from "ol/source/Vector"; +import Collection from "ol/Collection"; +import Style from "ol/style/Style"; +import Fill from "ol/style/Fill"; +import Stroke from "ol/style/Stroke"; +import Circle from "ol/style/Circle"; +import Overlay from "ol/Overlay"; +import OlSelect from "ol/interaction/Select"; +import OlModify from "ol/interaction/Modify"; +import Feature from "ol/Feature"; +import { Polygon } from "ol/geom"; +import { + Area, + MyFeature, + TModify, + TOlMap, + TSelect, +} from "@/app/types/facadeViewCanvas"; +import { ref, watch } from "vue"; +import useWindowSize from "./useWindowSize"; +//import { Coordinate } from "ol/coordinate"; + +/*export type Facade = { + id: string; + image: string; + figures: Area[]; +};*/ + +export default function useViewFacades() { + const {isMobile} = useWindowSize() + const mapObj = ref(null); + const activeFacade = ref(0); + const currentFacade = ref(null); + const facades = ref(null) + const optionsFloor = ref({ + floor: null, + entrance: null, + rooms: [], + id: null, + }) + const openedFigure = ref({ + floor: null, + entrance: null, + rooms: [], + id: null, + }) + const openModal = ref(false); + const existingOverlays = ref(null); + + const opacity = ref(); + + const emptyHoverStyle = ref() + const hoverStyle = ref(); + + watch(currentFacade, () => { + opacity.value = currentFacade.value.opacity === 1 ? 0.99 : currentFacade.value.opacity + hoverStyle.value = new Style({ + fill: new Fill({ + color: mapObj.value.hexToRgba(currentFacade.value.hover_color, 1 - opacity.value), + }), + stroke: new Stroke({ + color: mapObj.value.hexToRgba(currentFacade.value.hover_color), + width: 2, + }), + image: new Circle({ + radius: 7, + fill: new Fill({ + color: mapObj.value.hexToRgba(currentFacade.value.hover_color), + }), + }), + }); + }) + + watch(mapObj, (mapObjValue) => { + emptyHoverStyle.value = new Style({ + fill: new Fill({ + color: mapObjValue.hexToRgba('#adadad', 0.8), + }), + stroke: new Stroke({ + color: mapObjValue.hexToRgba('#adadad'), + width: 2, + }), + image: new Circle({ + radius: 7, + fill: new Fill({ + color: mapObjValue.hexToRgba('#adadad'), + }), + }), + }) + }) + + function start(facadesList) { + if (facadesList) facades.value = facadesList + + currentFacade.value = facades.value[activeFacade.value]; + mapObj.value = new OlMap(currentFacade.value.image, "map", function ( + olMap + ) { + onMapLoad(olMap); + }); + window.addEventListener("resize", () => { + if (mapObj.value) { + mapObj.value.resize(); + } + }); + } + + function OlMap(imageUrl, target, onMapLoad) { + this.target = target; + this.imageElement = new Image(); + + this.imageElement.onload = () => { + this.onImageLoad(); + onMapLoad(this); + }; + + this.imageElement.src = imageUrl; + } + + OlMap.prototype.onImageLoad = function () { + this.createMap(this.imageElement, this.target); + }; + + OlMap.prototype.createMap = function ( + imageElement, + target + ) { + this.imageLayer = this.createImageLayer(imageElement); + + const targetElement = document.getElementById(target); + const view = this.createView(imageElement, targetElement); + + this.vector = this.createVector(); + + this.map = new Map({ + layers: [this.imageLayer, this.vector], + overlays: [], + controls: ControlDefault({ + zoom: false, + rotate: false, + }), + target: "map", + view: view, + pixelRatio: 1, + interactions: InteractionDefault({ + mouseWheelZoom: false, + dragPan: false, + }), + }); + + this.addInteractionsToMap(this.map, this.vector); + this.addEvents(); + + const containerHeight = targetElement.clientHeight; + const imageWidth = imageElement.width; + const imageHeight = imageElement.height; + const mapWidth = (containerHeight / imageHeight) * imageWidth; + this.map.setSize([mapWidth, containerHeight]); + if (existingOverlays.value) { + mapObj.value.map.addOverlay(existingOverlays.value); + this.overlay = existingOverlays.value; + } else { + this.overlay = this.createOverlay(document.getElementById("popup")); + mapObj.value.map.addOverlay(this.overlay); + mapObj.value.overlay.setPosition(undefined) + } + }; + + OlMap.prototype.createImageLayer = function ( + imageElement + ) { + const imageStatic = this.createImageStatic(imageElement); + + return new ImageLayer({ + source: imageStatic, + }); + }; + OlMap.prototype.createImageStatic = function ( + imageElement + ) { + return new Static({ + url: imageElement.src, + imageExtent: [0, 0, imageElement.width, imageElement.height], + }); + }; + OlMap.prototype.createView = function ( + imageElement, + targetElement + ) { + const projection = this.createProjection(imageElement); + const containerHeight = document.getElementById("map").clientHeight; + const imageResolution = imageElement.naturalHeight / containerHeight; + + return new View({ + resolution: imageResolution, + projection: projection, + center: getCenter([0, 0, imageElement.width, imageElement.height]), + zoom: 0, + }); + }; + + OlMap.prototype.createProjection = function ( + imageElement + ) { + return new Projection({ + code: "olimage", + units: "pixels", + extent: [0, 0, imageElement.width, imageElement.height], + }); + }; + + OlMap.prototype.createVector = function () { + this.sourceVector = this.createSourceVector(); + + const style = this.createStyle(); + + return new VectorLayer({ + source: this.sourceVector, + style: style, + }); + }; + + OlMap.prototype.createSourceVector = function () { + this.features = new Collection(); + + return new VectorSource({ + features: this.features, + }); + }; + + OlMap.prototype.createStyle = function () { + return new Style({ + fill: new Fill({ + color: this.hexToRgba(currentFacade.value.hover_color, 0.2), + }), + image: new Circle({ + radius: 7, + fill: new Fill({ + color: this.hexToRgba(currentFacade.value.hover_color), + }), + }) + }) + }; + + OlMap.prototype.createOverlay = function ( + container + ){ + return new Overlay({ + element: container, + autoPan: { + animation: { + duration: 0, + }, + }, + }); + }; + + OlMap.prototype.addInteractionsToMap = function ( + map, + vector + ) { + this.modify = new Modify(map); + this.modify.setActive(true); + }; + + OlMap.prototype.addEvents = function () { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const mapObj = this; + this.map.on("click", function (event) { + let currentFeature; + this.forEachFeatureAtPixel( + event.pixel, + function (feature) { + if (feature.getGeometry().getType() == "Polygon") { + currentFeature = feature; + } + } + ); + if (currentFeature) { + if (openModal.value) openModal.value = false; + + if (openedFigure.value.id) { + closeOverlay() + + openedFigure.value = { + floor: null, + entrance: null, + rooms: [], + id: null, + } + } else { + openedFigure.value = { + floor: currentFeature.get("floor"), + entrance: currentFeature.get("entrance"), + rooms: currentFeature.get("rooms"), + id: currentFeature.getId() + }; + + showOverLay(currentFeature) + } + } else { + closeOverlay() + + openedFigure.value = { + floor: null, + entrance: null, + rooms: [], + id: null, + } + openModal.value = false + } + }); + + this.map.on("pointermove", function (event) { + if (openedFigure.value.id) return; + let currentFeature; + this.forEachFeatureAtPixel( + event.pixel, + function (feature) { + if (feature.getGeometry().getType() == "Polygon") { + currentFeature = feature; + } + } + ); + + if ( + currentFeature && + mapObj.features.getArray().indexOf(currentFeature) > -1 + ) { + optionsFloor.value = { + floor: currentFeature.get("floor"), + entrance: currentFeature.get("entrance"), + rooms: currentFeature.get("rooms"), + id: currentFeature.getId() + }; + mapObj.hovered = true; + currentFeature.setStyle(currentFeature.get("rooms").filter((room) => room.visible).length ? hoverStyle.value : emptyHoverStyle.value); + mapObj.removeStyleFromFeatures(currentFeature, mapObj); + } else if (mapObj.hovered) { + mapObj.hovered = false; + mapObj.removeStyleFromFeatures(null, mapObj); + } + }); + }; + + OlMap.prototype.hexToRgba = function ( + hex, + opacity + ) { + if (!hex) return; + hex = hex.replace("#", ""); + + const color = { + r: parseInt(hex.substring(0, 2), 16), + g: parseInt(hex.substring(2, 4), 16), + b: parseInt(hex.substring(4, 6), 16), + }; + + if (opacity) { + opacity = opacity > 1 ? opacity / 100 : opacity; + return ( + "rgba(" + + color.r + + ", " + + color.g + + ", " + + color.b + + ", " + + opacity + + ")" + ); + } else { + return "rgba(" + color.r + ", " + color.g + ", " + color.b + ")"; + } + }; + + OlMap.prototype.removeStyleFromFeatures = function ( + currentFeature, + mapObj + ) { + this.sourceVector?.getFeatures().forEach(function (feature) { + if (currentFeature) { + if (feature.getId() !== currentFeature.getId()) { + feature.setStyle(mapObj.createStyle()); + } + } else { + feature.setStyle(mapObj.createStyle()); + } + }); + }; + + OlMap.prototype.resize = function () { + if (this.map) { + const image = this.imageElement; + const container = document.getElementById("map"); + + if (container) { + const containerHeight = container.clientHeight; + const imageResolution = image.naturalHeight / containerHeight; + this.map.getView().setResolution(imageResolution); + } + } + }; + + OlMap.prototype.setColor = function ( + color, + opacity + ) { + this.color = color; + this.opacity = opacity || 20; + + const style = this.vector.getStyle(); + style.getFill().setColor(this.hexToRgba(this.color, this.opacity)); + }; + + /* Interactions */ + + function Modify(map) { + this.interaction = new OlModify({ + features: mapObj.value ? mapObj.value.features : new Collection(), + // deleteCondition: function (event) { + // return shiftKeyOnly(event) && singleClick(event); + // }, + }); + + // map.addInteraction(this.interaction); + } + + Modify.prototype.setActive = function (active) { + // this.interaction.setActive(active); + }; + + function Select(map) { + this.interaction = new OlSelect(); + map.addInteraction(this.interaction); + } + + Select.prototype.setActive = function (active) { + this.interaction.setActive(active); + }; + + Select.prototype.clear = function () { + const selectedFeatures = this.interaction.getFeatures(); + + // selectedFeatures.forEach(selectedFeatures.remove, selectedFeatures); + }; + + function onMapLoad(mapObj) { + initFeatures(); + + addMapEvents(mapObj); + + colorInit(); + } + + function changeFeatureRooms(id, rooms) { + mapObj.value.features.forEach((feature) => { + if (feature.getId() == id) { + feature.set('rooms', rooms) + } + }) + } + + function initFeatures() { + const figures = currentFacade.value.figures; + figures.forEach(function (item) { + const polygon = new Polygon([item.points]); + const feature = new Feature({ + geometry: polygon, + }); + + feature.setStyle(mapObj.value.createStyle()); + + feature.setId(item.id); + feature.set("entrance", item.entrance); + feature.set("floor", item.floor); + feature.set('rooms', item.rooms) + + try { + mapObj.value.sourceVector.addFeature(feature); + } catch (e) { + location.reload(); + } + }); + } + + function addMapEvents(mapObj) { + if (!mapObj.value) return; + mapObj.value.map + .getViewport() + .addEventListener("mousemove", function (event) { + event.preventDefault(); + }); + mapObj.value.map.getViewport().addEventListener("click", function (event) { + event.preventDefault(); + }); + } + + function showOverLay(feature) { + const popup = document.querySelector("#popup"); + + if (popup.style.left != 'unset') { + popup.style.left = 'unset' + } + // mapObj.value.select.interaction.getFeatures().push(feature); + // mapObj.value.selectFeature = feature; + + const coordinates = feature.getGeometry().getCoordinates()[0]; + let currentCoordinate = coordinates[0]; + + coordinates.forEach(function (coord) { + if (coord[0] > currentCoordinate[0] && coord[1] > currentCoordinate[1]) { + currentCoordinate = coord; + } + }); + console.log(mapObj.value.overlay.element.classList.add('mobile')); + + mapObj.value.overlay.setPosition(currentCoordinate); + // const popup = document.querySelector("#popup"); + popup.style.display = "block"; + } + + function closeOverlay() { + mapObj.value.overlay.setPosition(undefined); + mapObj.value.selectFeature = null; + + return false; + } + + const colorInit = () => { + mapObj.value.setColor(currentFacade.value.hover_color, currentFacade.value.opacity); + }; + + const changeFacade = (type) => { + if (type === "next") { + activeFacade.value++; + if (activeFacade.value === facades.value.length) activeFacade.value = 0; + currentFacade.value = facades.value[activeFacade.value]; + } + if (type === "prev") { + if (activeFacade.value === 0) { + activeFacade.value = facades.value.length - 1; + } else { + activeFacade.value--; + } + currentFacade.value = facades.value[activeFacade.value]; + } + + existingOverlays.value = mapObj.value.map + .getOverlays() + .getArray() + .slice()[0]; + + mapObj.value.map.setTarget(null); + mapObj.value = null; + + start(); + }; + return { + start, + optionsFloor, + changeFacade, + currentFacade, + openedFigure, + openModal, + closeOverlay, + changeFeatureRooms + }; +} diff --git a/src/features/useViewFloors.js b/src/features/useViewFloors.js new file mode 100644 index 0000000..eb8dc6a --- /dev/null +++ b/src/features/useViewFloors.js @@ -0,0 +1,733 @@ +import Map from "ol/Map"; +import ZoomSlider from "ol/control/ZoomSlider"; +import Zoom from "ol/control/Zoom"; +import VectorLayer from "ol/layer/Vector"; +import VectorSource from "ol/source/Vector"; +import Collection from "ol/Collection"; +import Style from "ol/style/Style"; +import Fill from "ol/style/Fill"; +import Stroke from "ol/style/Stroke"; +import Circle from "ol/style/Circle"; +import View from "ol/View"; +import ImageLayer from "ol/layer/Image"; +import Static from "ol/source/ImageStatic"; +import { getCenter } from "ol/extent"; +import { Projection } from "ol/proj"; +import Overlay from "ol/Overlay"; +import OlModify from "ol/interaction/Modify"; +import { shiftKeyOnly, singleClick } from "ol/events/condition"; +import OlSelect from "ol/interaction/Select"; +import OlDraw from "ol/interaction/Draw"; +import { toStringXY } from "ol/coordinate"; // Coordinate, +import OlSnap from "ol/interaction/Snap"; +import { Polygon } from "ol/geom"; // Geometry, Point, +import Feature, { FeatureLike } from "ol/Feature"; +// import { Pixel } from "ol/pixel"; +// import { Area, TDraw, TModify, TSelect } from "@/app/types/facadeViewCanvas"; +import { nextTick, ref } from "vue"; + +/*type TOlMap = { + map: Map; + features: any; + sourceVector: VectorSource; + draw: TDraw; + modify: TModify; + select: TSelect; + snap: OlSnap; + imageElement: HTMLImageElement; + imageLayer: ImageLayer; + target: string; + vector: VectorLayer; + overlay: Overlay; + color: string; + opacity: number; + hovered: boolean; + zoomSliderWrapper: HTMLElement; + popupWrapper: HTMLElement; + isMouseOverMap: boolean; + onImageLoad: () => void; + createMap: (imageElement: HTMLImageElement, target: string) => void; + addInteractionsToMap: (map: Map, vector: VectorLayer) => void; + setColor: (color: string, opacity: number) => void; + hexToRgba: (hex: string, opacity?: number) => string; + addEvents: () => void; + removeStyleFromFeatures: (currentFeature?: Feature) => void; + createVector: () => VectorLayer; + createSourceVector: () => VectorSource; + createStyle: () => Style; + createView: (imageElement: HTMLImageElement) => View; + createProjection: (imageElement: HTMLImageElement) => Projection; + createImageLayer: (imageElement: HTMLImageElement) => ImageLayer; + createImageStatic: (imageElement: HTMLImageElement) => Static; + createOverlay: (container: HTMLElement) => Overlay; +};*/ +export default function useViewFloors() { + const olMap = ref(); + const openWindowInfo = ref(false); + const windowInfoData = ref({}); + const areas = ref([]) + const image = ref(null) + const hoveredFigure = ref(null) + + + function start( + map, + container, + zoomSliderWrapper, + popupWrapper, + imageUrl, + figures + ) { + areas.value = figures + image.value = imageUrl + + olMap.value = new OlMap( + image.value, + map || "map", + zoomSliderWrapper, + popupWrapper, + function (olMap) { + onMapLoad(olMap); + } + ); + + // setMapHeight(map, container); + } + + // const setMapHeight = function (map, container) { + // const scrollTop = + // window.pageYOffset || + // document.documentElement.scrollTop || + // document.body.scrollTop; + + // const marginY = + // parseInt(container.style.marginTop) + + // parseInt(container.style.marginBottom); + + // map.style.height = container.clientHeight - + // (map.getBoundingClientRect().top - scrollTop) - + // marginY + + // "px"; + // }; + + function OlMap( + + imageUrl, + target, + zoomContainer, + popupWrapper, + onMapLoad + ) { + this.target = target; + this.imageElement = new Image(); + this.zoomSliderWrapper = zoomContainer; + this.popupWrapper = popupWrapper; + + this.imageElement.onload = () => { + this.onImageLoad(); + onMapLoad(this); + }; + + this.imageElement.src = imageUrl; + } + + OlMap.prototype.onImageLoad = function () { + this.createMap(this.imageElement, this.target); + }; + + OlMap.prototype.createMap = function ( + + imageElement, + target + ) { + document.querySelector('.ol-map__zoom')?.remove() + document.querySelector('.ol-map__pane')?.remove() + + this.imageLayer = this.createImageLayer(imageElement); + + const targetElement = document.getElementById(target); + const view = this.createView(imageElement); + + this.vector = this.createVector(); + this.overlay = this.createOverlay(this.popupWrapper); + + const paneNode = document.createElement("div"); + paneNode.className = "ol-map__pane"; + + targetElement.appendChild(paneNode); + + this.map = new Map({ + layers: [this.imageLayer, this.vector], + overlays: [this.overlay], + controls: [], + target: paneNode, + view: view, + pixelRatio: 1, + }); + + const zoomNode = document.createElement("div"); + zoomNode.className = "ol-map__zoom"; + + const zoomSlider = new ZoomSlider({ + className: "ol-map__zoom-slider", + }); + + this.zoomSliderWrapper.prepend(zoomNode); + + zoomSlider.setTarget(zoomNode); + this.map.addControl(zoomSlider); + + const ctrlButtons = new Zoom({ + target: zoomNode, + className: "ol-map__zoom-btns", + zoomInLabel: "+", + zoomOutLabel: "\u2212", + delta: 1, + }); + + const zoomLabelNode = document.createElement("div"); + zoomLabelNode.className = "ol-map__zoom-label"; + zoomLabelNode.textContent = "масштаб"; + + zoomNode.appendChild(zoomLabelNode); + this.map.addControl(ctrlButtons); + this.addInteractionsToMap(this.map, this.vector); + this.addEvents(); + }; + + OlMap.prototype.addInteractionsToMap = function ( + + map, + vector + ) { + // this.modify = new Modify(map); + // this.modify.setActive(true); + + // this.select = new Select(map); + // this.select.setActive(true); + + // this.draw = new Draw(map, vector); + // this.draw.setActive(true); + + this.snap = new Snap(map, vector); + }; + + OlMap.prototype.setColor = function ( + + color, + opacity + ) { + this.color = color; + this.opacity = opacity || 20; + + const style = this.vector.getStyle(); + style.getFill().setColor(this.hexToRgba(this.color, this.opacity / 100)); + }; + + OlMap.prototype.hexToRgba = function ( + + hex, + opacity + ){ + if (!hex) return; + hex = hex.replace("#", ""); + + const color = { + r: parseInt(hex.substring(0, 2), 16), + g: parseInt(hex.substring(2, 4), 16), + b: parseInt(hex.substring(4, 6), 16), + }; + + if (opacity) { + opacity = opacity > 1 ? opacity / 100 : opacity; + return ( + "rgba(" + + color.r + + ", " + + color.g + + ", " + + color.b + + ", " + + opacity + + ")" + ); + } else { + return "rgba(" + color.r + ", " + color.g + ", " + color.b + ")"; + } + }; + + /* Events */ + + OlMap.prototype.addEvents = function () { + this.map.on("click", function (event) { + let currentFeature; + this.forEachFeatureAtPixel( + event.pixel, + function (feature) { + if (feature.getGeometry().getType() == "Polygon") { + currentFeature = feature; + } + } + ); + openWindowInfo.value = true; + windowInfoData.value = {}; + }); + + this.map.getViewport().addEventListener("mouseover", () => { + this.isMouseOverMap = true; + }); + + this.map.getViewport().addEventListener("mouseout", () => { + this.isMouseOverMap = false; + }); + + this.map.getViewport().addEventListener("wheel", (event) => { + if (!this.isMouseOverMap) { + event.preventDefault(); + } + }); + + this.map.on("pointermove", function (event) { + let currentFeature; + + this.forEachFeatureAtPixel( + event.pixel, + function (feature) { + if (feature.getGeometry().getType() == "Polygon") { + currentFeature = feature; + } + } + ); + + olMap.value.removeStyleFromFeatures(currentFeature) + + if ( + currentFeature && + olMap.value.features.getArray().indexOf(currentFeature) > -1 + ) { + olMap.value.hovered = true; + showOverLay(currentFeature); + this.getTargetElement().style.cursor = "pointer"; + } else if (olMap.value.hovered) { + closeOverlay(); + olMap.value.hovered = false; + this.getTargetElement().style.cursor = "default"; + } + }); + }; + + OlMap.prototype.removeStyleFromFeatures = function ( + + currentFeature + ) { + this.sourceVector?.getFeatures().forEach(function (feature) { + if (feature !== currentFeature) { + feature.setStyle( + new Style({ + fill: new Fill({ + color: olMap.value.hexToRgba(feature.get('visible') ? "#4caf50" : "#cfcfcf", 0.7), + }), + }) + ); + } + }); + }; + + /* Vector, Style */ + + OlMap.prototype.createVector = function ( + + ){ + this.sourceVector = this.createSourceVector(); + + const style = this.createStyle(); + + return new VectorLayer({ + source: this.sourceVector, + style: style, + }); + }; + + OlMap.prototype.createSourceVector = function () { + this.features = new Collection(); + + return new VectorSource({ + features: this.features, + }); + }; + + OlMap.prototype.createStyle = function () { + return new Style({ + fill: new Fill({ + color: "rgba(255, 56, 56, 0.2)", + }), + stroke: new Stroke({ + color: "#ffcc33", + width: 0.1, + }), + image: new Circle({ + radius: 7, + fill: new Fill({ + color: "#ffcc33", + }), + }), + }); + }; + + /* View, Layer, Image */ + + OlMap.prototype.createView = function ( + + imageElement + ) { + const projection = this.createProjection(imageElement); + + return new View({ + projection: projection, + center: getCenter([0, 0, imageElement.width, imageElement.height]), + zoom: 0, + minZoom: 1, + maxZoom: 6, + }); + }; + + OlMap.prototype.createProjection = function ( + + imageElement + ) { + return new Projection({ + code: "olimage", + units: "pixels", + extent: [0, 0, imageElement.width, imageElement.height], + }); + }; + + OlMap.prototype.createImageLayer = function ( + + imageElement + ) { + const imageStatic = this.createImageStatic(imageElement); + + return new ImageLayer({ + source: imageStatic, + }); + }; + OlMap.prototype.createImageStatic = function ( + + imageElement + ) { + return new Static({ + url: imageElement.src, + imageExtent: [0, 0, imageElement.width, imageElement.height], + }); + }; + + OlMap.prototype.createOverlay = function ( + + container + ) { + return new Overlay({ + element: container, + autoPan: true, + insertFirst: true, + }); + }; + + /* Interactions */ + + + function Modify(map) { + this.interaction = new OlModify({ + features: olMap.value ? olMap.value.features : new Collection(), + deleteCondition: function (event) { + return shiftKeyOnly(event) && singleClick(event); + }, + }); + + map.addInteraction(this.interaction); + } + + Modify.prototype.setActive = function (active) { + this.interaction.setActive(active); + }; + + + function Select(map) { + this.interaction = new OlSelect(); + map.addInteraction(this.interaction); + this.setEvents(); + } + + Select.prototype.setEvents = function () { + this.interaction.on( + "change:active", + function () { + this.clear(); + }.bind(this) + ); + }; + + Select.prototype.setActive = function (active) { + this.interaction.setActive(active); + }; + + Select.prototype.clear = function () { + const selectedFeatures = this.interaction.getFeatures(); + + // selectedFeatures.forEach(selectedFeatures.remove, selectedFeatures); + }; + + /** + * type TDraw + */ + + + function Draw(map, vector) { + this.interaction = new OlDraw({ + source: vector.getSource(), + type: /** @type {ol.geom.GeometryType} */ "Polygon", + freehand: false, + condition: function (event) { + // Left Click + return event.originalEvent.which == 1; + }, + }); + + this.interaction.setActive(false); + map.addInteraction(this.interaction); + } + + Draw.prototype.coordToString = function (coordXY) { + return toStringXY(coordXY, 3); + }; + + Draw.prototype.isOnePoint = function ( + + coords + ) { + return coords.every( + function (coordXY) { + return this.coordToString(coords[0]) == this.coordToString(coordXY); + }.bind(this) + ); + }; + + Draw.prototype.getFeature = function () { + // return this.interaction.sketchFeature_; + }; + + Draw.prototype.getActive = function () { + return this.interaction && this.interaction.getActive(); + }; + + Draw.prototype.setActive = function (active) { + this.interaction.setActive(active); + }; + + function Snap(map, vector) { + const snap = new OlSnap({ + source: vector.getSource(), + }); + + map.addInteraction(snap); + } + + const colorInit = () => { + olMap.value.setColor("#FF3838", 0.2); + }; + + function onMapLoad(olMap) { + initFeatures(); + + addMapEvents(); + + colorInit(); + } + + + function initFeatures() { + areas.value.forEach(function (item) { + // const feature = new Feature({ + // geometry: new Polygon([item.points]), + // }); + + const feature = new Feature(new Polygon([item.points])); + + feature.setId(item.id); + feature.set('visible', item.visible) + feature.setStyle( + new Style({ + fill: new Fill({ + color: olMap.value.hexToRgba(item.visible ? "#4caf50" : "#cfcfcf", 0.7), + }), + }) + ); + + const geo = feature.getGeometry() + const center = getCenter(geo.getExtent()); + const imageCenter = getCenter(olMap.value.imageLayer.values_.source.getImageExtent()) + // geo.setFlatCoordinates([item.points.map(([x, y]) => [x, y ])]) + geo.scale(1, -1, imageCenter) + // geo.rotate(270, center) + + try { + olMap.value.sourceVector.addFeature(feature); + } catch (e) { + location.reload(); + } + }); + } + + function addMapEvents() { + if (!olMap.value) return; + olMap.value.map + .getViewport() + .addEventListener("contextmenu", function (event) { + event.preventDefault(); + + // rightClickHandler(event); + }); + + // olMap.value.draw.interaction.on("change", function (event) { + // console.log(event); + // }); + + // olMap.value.draw.interaction.on("drawstart", function (event) { + // console.log(event); + // }); + + // olMap.value.draw.interaction.on("drawend", function (event) { + // const feature: Feature = event.feature as Feature; + // addFacade(feature); + // }); + + // olMap.value.modify.interaction.on("modifyend", function (event) { + // const calls = []; + // }); + + olMap.value.map.on("dblclick", function (e) { + if (olMap.value.draw?.interaction.getPointerCount()) { + olMap.value.draw.interaction.removeLastPoint(); + olMap.value.draw.interaction.removeLastPoint(); + } + + showOverlayAtPixel(olMap.value.map.getEventPixel(e.originalEvent)); + }); + } + + function rightClickHandler(event) { + if (olMap.value.draw.interaction.getPointerCount() !== 0) { + olMap.value.draw.interaction.removeLastPoint(); + return; + } + + if (olMap.value.draw.interaction.getPointerCount() !== 0) { + return; + } + + showOverlayAtPixel(olMap.value.map.getEventPixel(event)); + } + + function showOverlayAtPixel(pixel) { + const feature = getFeatureAtPixel(pixel); + + if (!feature) { + return; + } + } + + function getFeatureAtPixel(pixel) { + let polygonFeature; + + olMap.value.map.forEachFeatureAtPixel(pixel, function (feature, layer) { + if ( + feature.getGeometry().getType() == "Polygon" && + olMap.value.features.getArray().indexOf(feature) > + -1 + ) { + polygonFeature = feature; + } + }); + + return polygonFeature; + } + + // function addFacade(feature) { + // const geom = feature.getGeometry(); + + // const id = Math.random().toString(16).slice(2); + // const data = { + // id, + // points: geom.getCoordinates()[0], + // }; + + // areas.push(data); + + // localStorage.setItem("areas", JSON.stringify(areas)); + + // showOverLay(feature); + // } + + function showOverLay(feature) { + closeOverlay() + // onOverlayToggled(feature.id_) + hoveredFigure.value = feature + // olMap.value.select.clear(); + // olMap.value.select.interaction.getFeatures().push(feature); + + const geo = feature.getGeometry(); + const center = getCenter(geo.getExtent()); + + // hoveredFigure.value.visible + feature.setStyle( + new Style({ + fill: new Fill({ + color: olMap.value.hexToRgba("#cfcfcf", 0.1), + }), + stroke: new Stroke({ + color: "#FF3838", + width: 0.1, + }), + }) + ); + nextTick(() => { + if (!hoveredFigure.value.visible) { + return + } + olMap.value.overlay.setPosition(center); + olMap.value.overlay.setPositioning("top-center"); + + addOverlayEvents(feature); + }) + } + + function addOverlayEvents(feature) { + const popup = document.querySelector("#popup"); + popup.addEventListener("mousedown", (e) => { + e.stopPropagation(); + e.preventDefault(); + }); + } + + function closeOverlay(feature) { + if (hoveredFigure.value) { + hoveredFigure.value.setStyle( + new Style({ + fill: new Fill({ + color: olMap.value.hexToRgba(hoveredFigure.value.visible ? "#4caf50" : "#cfcfcf", 0.7), + }), + }) + ); + } + hoveredFigure.value = null + olMap.value.overlay.setPosition(undefined); + return false; + } + + return { + openWindowInfo, + windowInfoData, + start, + hoveredFigure, + }; +} diff --git a/src/features/useWindowSize.js b/src/features/useWindowSize.js new file mode 100644 index 0000000..5611ff4 --- /dev/null +++ b/src/features/useWindowSize.js @@ -0,0 +1,26 @@ +import { ref, onMounted, onUnmounted } from "vue"; + +export default function useWindowSize() { + const isMobile = ref(false); + const isTablet = ref(false); + + const updateSize = () => { + const width = window.innerWidth; + isMobile.value = width <= 408; + isTablet.value = width > 408 && width <= 768; + }; + + onMounted(() => { + updateSize(); // Initial check + window.addEventListener("resize", updateSize); + }); + + onUnmounted(() => { + window.removeEventListener("resize", updateSize); + }); + + return { + isMobile, + isTablet + }; +} \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..7c97aca --- /dev/null +++ b/src/main.js @@ -0,0 +1,77 @@ +import { createApp } from "vue"; +import App from "@/app/App.vue"; +import { createPinia } from "pinia"; +import router from "@/app/router"; +import PrimeVue from "primevue/config"; + +import ConfirmationService from "primevue/confirmationservice"; + + +//Components +import InputText from "primevue/inputtext"; +import Dropdown from "primevue/dropdown"; +import Textarea from "primevue/textarea"; +import MultiSelect from "primevue/multiselect"; +import Calendar from "primevue/calendar"; +import InputSwitch from "primevue/inputswitch"; +import Message from "primevue/message"; +import primeVueSettings from "./shared/utils/primeVueSettings"; +import MyButton from "@/shared/UI/MyButton.vue"; +import Tooltip from "primevue/tooltip"; +import DataTable from "primevue/datatable"; +import Column from "primevue/column"; +import Button from "primevue/button"; +import TabView from "primevue/tabview"; +import TabPanel from "primevue/tabpanel"; +import Dialog from "primevue/dialog"; +import Galleria from 'primevue/galleria'; +import Paginator from 'primevue/paginator'; +import Carousel from 'primevue/carousel'; +import VueCropper from 'vue-cropperjs'; +import ToastService from 'primevue/toastservice' +import Editor from 'primevue/editor'; +import 'cropperjs/dist/cropper.css'; +import { createYmaps } from "vue-yandex-maps"; + +const app = createApp(App); +const pinia = createPinia(); + +app.component("VueCropper", VueCropper); +app.component("InputText", InputText); +app.component("Carousel", Carousel); +app.component("Dropdown", Dropdown); +app.component("Textarea", Textarea); +app.component("MultiSelect", MultiSelect); +app.component("Calendar", Calendar); +app.component("InputSwitch", InputSwitch); +app.component("Message", Message); +app.component("DataTable", DataTable); +app.component("Column", Column); +app.component("Button", Button); +app.component("MyButton", MyButton); +app.component("TabView", TabView); +app.component("TabPanel", TabPanel); +app.component("Dialog", Dialog); +app.component("Galleria", Galleria); +app.component("Paginator", Paginator); +app.component('Editor', Editor); + +app.directive("tooltip", Tooltip); + +app.use(pinia); +app.use(router); +app.use(ToastService) +app.use(PrimeVue, primeVueSettings); +app.use(ConfirmationService, {}); +app.use(createYmaps({ + apikey: '692f0a05-cb88-4158-88b9-06bc0dc93004', + lang: 'ru-RU', + // load: 'package.standard', + // ns: 'yandexMap' + // &onload=onYandexMapInit +})); +app.mount("#app"); + + +import eventBus from "./eventBus"; +window.complexBus = eventBus \ No newline at end of file diff --git a/src/pages/Apartment.vue b/src/pages/Apartment.vue new file mode 100644 index 0000000..cd0a661 --- /dev/null +++ b/src/pages/Apartment.vue @@ -0,0 +1,462 @@ + + + + + \ No newline at end of file diff --git a/src/pages/ApartmentDetailIframe.vue b/src/pages/ApartmentDetailIframe.vue new file mode 100644 index 0000000..8f1fbbf --- /dev/null +++ b/src/pages/ApartmentDetailIframe.vue @@ -0,0 +1,494 @@ + + + + + \ No newline at end of file diff --git a/src/pages/ApartmentIframe.vue b/src/pages/ApartmentIframe.vue new file mode 100644 index 0000000..0563087 --- /dev/null +++ b/src/pages/ApartmentIframe.vue @@ -0,0 +1,226 @@ + + + + + \ No newline at end of file diff --git a/src/pages/ApartmentListIframe.vue b/src/pages/ApartmentListIframe.vue new file mode 100644 index 0000000..dbf88f7 --- /dev/null +++ b/src/pages/ApartmentListIframe.vue @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/src/pages/ApartmentsList.vue b/src/pages/ApartmentsList.vue new file mode 100644 index 0000000..6669cbd --- /dev/null +++ b/src/pages/ApartmentsList.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/src/pages/ComplexPage.vue b/src/pages/ComplexPage.vue new file mode 100644 index 0000000..b49f420 --- /dev/null +++ b/src/pages/ComplexPage.vue @@ -0,0 +1,769 @@ + + + + + + + diff --git a/src/pages/ComplexViewMode/index.vue b/src/pages/ComplexViewMode/index.vue new file mode 100644 index 0000000..8fb9585 --- /dev/null +++ b/src/pages/ComplexViewMode/index.vue @@ -0,0 +1,162 @@ + + + + + \ No newline at end of file diff --git a/src/pages/ComplexViewMode/widgets/HomeItem.vue b/src/pages/ComplexViewMode/widgets/HomeItem.vue new file mode 100644 index 0000000..d370e80 --- /dev/null +++ b/src/pages/ComplexViewMode/widgets/HomeItem.vue @@ -0,0 +1,300 @@ + + + + + + \ No newline at end of file diff --git a/src/pages/ComplexViewMode/widgets/ListHome.vue b/src/pages/ComplexViewMode/widgets/ListHome.vue new file mode 100644 index 0000000..6eb9589 --- /dev/null +++ b/src/pages/ComplexViewMode/widgets/ListHome.vue @@ -0,0 +1,499 @@ + + + + + + \ No newline at end of file diff --git a/src/pages/ComplexesView.vue b/src/pages/ComplexesView.vue new file mode 100644 index 0000000..870bf80 --- /dev/null +++ b/src/pages/ComplexesView.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/src/pages/Edit/index.vue b/src/pages/Edit/index.vue new file mode 100644 index 0000000..a99fb02 --- /dev/null +++ b/src/pages/Edit/index.vue @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/src/pages/EditFacades.vue b/src/pages/EditFacades.vue new file mode 100644 index 0000000..2b8fb50 --- /dev/null +++ b/src/pages/EditFacades.vue @@ -0,0 +1,508 @@ + + + + + diff --git a/src/pages/EditPlanFloors.vue b/src/pages/EditPlanFloors.vue new file mode 100644 index 0000000..500d775 --- /dev/null +++ b/src/pages/EditPlanFloors.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/src/pages/HomePage.vue b/src/pages/HomePage.vue new file mode 100644 index 0000000..23b0a65 --- /dev/null +++ b/src/pages/HomePage.vue @@ -0,0 +1,54 @@ + + diff --git a/src/pages/HomePage/ChessPage.vue b/src/pages/HomePage/ChessPage.vue new file mode 100644 index 0000000..06ba374 --- /dev/null +++ b/src/pages/HomePage/ChessPage.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/src/pages/HomePage/MainPage.vue b/src/pages/HomePage/MainPage.vue new file mode 100644 index 0000000..e20e47d --- /dev/null +++ b/src/pages/HomePage/MainPage.vue @@ -0,0 +1,378 @@ + + + + + + + diff --git a/src/pages/HomePage/PlanLayoutPage.vue b/src/pages/HomePage/PlanLayoutPage.vue new file mode 100644 index 0000000..55fe602 --- /dev/null +++ b/src/pages/HomePage/PlanLayoutPage.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/pages/HomePage/ViewHome.vue b/src/pages/HomePage/ViewHome.vue new file mode 100644 index 0000000..ab92dda --- /dev/null +++ b/src/pages/HomePage/ViewHome.vue @@ -0,0 +1,447 @@ + + + + + diff --git a/src/pages/ImportHomes.vue b/src/pages/ImportHomes.vue new file mode 100644 index 0000000..330ef74 --- /dev/null +++ b/src/pages/ImportHomes.vue @@ -0,0 +1,260 @@ + + + + + \ No newline at end of file diff --git a/src/pages/MainPage.vue b/src/pages/MainPage.vue new file mode 100644 index 0000000..b3d8932 --- /dev/null +++ b/src/pages/MainPage.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/pages/View/index.vue b/src/pages/View/index.vue new file mode 100644 index 0000000..8e9001a --- /dev/null +++ b/src/pages/View/index.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/src/shared/UI/MyButton.vue b/src/shared/UI/MyButton.vue new file mode 100644 index 0000000..1c7175c --- /dev/null +++ b/src/shared/UI/MyButton.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/src/shared/UI/fileUpload.vue b/src/shared/UI/fileUpload.vue new file mode 100644 index 0000000..bc730af --- /dev/null +++ b/src/shared/UI/fileUpload.vue @@ -0,0 +1,208 @@ + + + + + + diff --git a/src/shared/UI/formItemLayout.vue b/src/shared/UI/formItemLayout.vue new file mode 100644 index 0000000..4cb78da --- /dev/null +++ b/src/shared/UI/formItemLayout.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/src/shared/UI/mapsModalLayout.vue b/src/shared/UI/mapsModalLayout.vue new file mode 100644 index 0000000..9c83970 --- /dev/null +++ b/src/shared/UI/mapsModalLayout.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/shared/UI/modalLayout.vue b/src/shared/UI/modalLayout.vue new file mode 100644 index 0000000..5c9eedf --- /dev/null +++ b/src/shared/UI/modalLayout.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/src/shared/UI/requestModal.vue b/src/shared/UI/requestModal.vue new file mode 100644 index 0000000..fa3c579 --- /dev/null +++ b/src/shared/UI/requestModal.vue @@ -0,0 +1,234 @@ + + + + + \ No newline at end of file diff --git a/src/shared/api.js b/src/shared/api.js new file mode 100644 index 0000000..add3c28 --- /dev/null +++ b/src/shared/api.js @@ -0,0 +1,287 @@ +import axios from "axios" +// const URL = process.env.VUE_APP_API_URL; +// const URL = 'https://joywork.ru'; +// const URL = 'http://localhost:8080'; +// const URL = 'http://localhost'; +const URL = 'https://dev.joywork.ru'; + + +// console.log(URL, 'URL URL'); + +const api_instance = axios.create({ + baseURL: '/ajax/vue_ajax/complex_axios.php', + }); + +// const api_instance = axios.create({ +// baseURL: `${URL}/ajax/vue_ajax/complex_axios.php`, +// // withCredentials: true +// }) + +api_instance.defaults.headers.common['Access-Control-Allow-Origin'] = '*'; + +const get_user_id = () => { + if (window.auth_user_id) { + return window.auth_user_id + } + console.log(window.auth_user_id, 'window.auth_user_id'); + + window.auth_user_id = document.getElementById('session_user_id').value + return window.auth_user_id +} + +const callApi = (request, params = {}) => { + return api_instance.post('', { + request, + user_id: get_user_id(), + ...params + }) +} + +const callWithFormData = (request, formData) => { + formData.append("request", request); + formData.append("user_id", get_user_id()); + return api_instance.post('', formData, { + headers: { "Content-Type": "multipart/form-data" }, + }) +} + +// Entrances +const getHouseEntrances = (house_id, includes = []) => callApi('get_house_entrances', {includes, house_id}) + +const createEntrance = (house_id, fields ) => callApi('add_house_entrance', {house_id, fields}) + +const updateEntrance = (entrance_id, fields) => callApi('update_house_entrance', {entrance_id, fields}) + +const deleteEntrance = (entrance_id) => callApi('delete_house_entrance', {entrance_id}) + +const getComplexEntrances = (complex_id, includes = []) => callApi('get_complex_entrances', {includes, complex_id}) + +// Complexes +const getComplexOne = (complex_id, object_list = false) => callApi('get_complex_one', { complex_id, object_list}) + +const getComplex = (country_id, region_id) => callApi('get_complex', {country_id, region_id}) +const getComplexStatus = () => callApi('get_complex_status') +const searchComplexes = (filter = {}, page, use_empty_rooms) => callApi('search_for_iframe', {filter, page, use_empty_rooms}) +const getComplexTypes = () => callApi('get_types_complex') +const saveComplex = (complex, newBuilderPhone, newBuilderLink) => callApi('save_complex', {complex, newBuilderPhone, newBuilderLink }) +const archiveComplex = (payload) => callApi('complex_archive', payload) +const deleteComplex = (complex_id) => callApi('complex_delete', { complex_id }) + +const addPhotoToComplex = (formData) => callWithFormData('add_photos_to_complex', formData) +const updateComplexPhoto = (formData) => callWithFormData('update_complex_photo', formData) +const deleteComplexPhoto = (photo_id) => callApi('delete_complex_photo', {photo_id}) +// Houses +const getComplexHouses = (complex_id) => callApi('get_houses', {complex_id}) + +const getHouseOne = (house_id, includes = []) => callApi('get_house_one', {house_id, includes}) + +const getHouseTypes = () => callApi('get_house_types') + +const addEditHouse = (house) => callApi('add_edit_house', {house}) + +const addPhotoToHouse = (formData) => callWithFormData('add_photos_to_house', formData) +const deleteHousePhoto = (photo_id) => callApi('delete_house_photo', {photo_id}) +// Floors +const createFloor = (entrance_id, fields) => callApi('create_floor', {fields, entrance_id}) +const updateFloor = (floor_id, fields) => callApi('update_floor', {floor_id, fields}) +const deleteFloor = (floor_id) => callApi('delete_floor', {floor_id}) +const copyFloor = (floor_id, position) => callApi('copy_floor', {floor_id, position}) + +// Floor Plans +const getFloorPlans = (house_id) => callApi('get_house_floors_plans', {house_id}) +const getFloorPlan = (floor_plan_id) => callApi('get_floor_plan', {floor_plan_id}) +const getHouseFloorPlans = (house_id) => callApi('get_entrances_floor_plans', {house_id}) +const updateFloorPlans = (plan_ids = [], floor_ids = [], fields, use_bulk = false) => callApi('update_house_floor_plans', {plan_ids, floor_ids, fields, use_bulk}) +const deleteFloorPlan = (floor_plan_id) => callApi('delete_floor_plan', {floor_plan_id}) +const addFloorPlan = (formData) => callWithFormData('add_floor_plan_to_house', formData) +const updateFloorPlanPhoto = (formData) => callWithFormData('update_photo_floor_plan', formData) +// Floor Plan Figures +const deleteFloorPlanFigure = (figure_id) => callApi('delete_floor_plan_figure', {figure_id}) + +const addFigureToFloorPlan = (floor_plan_id, fields) => callApi('add_floor_plan_figure', {floor_plan_id, fields}) + +const updateFigure = (figure_id, fields) => callApi('update_floor_plan_figure', {figure_id, fields}) + +// Facades +const getFacades = (house_id, includes = []) => callApi('get_house_facades', {house_id, includes}) + +const deleteHouseFacade = (facade_id) => callApi('delete_house_facade', {facade_id}) + +const addFacadeToHouse = (formData) => callWithFormData('add_facade_to_house', formData) + +const updateFacade = (facade_id, fields) => callApi('update_house_facade', {facade_id, fields}) + +// Facade Figures +const addFigureToFacade = (facade_id, points) => callApi('add_facade_figure', {facade_id, points}) +const deleteFacadeFigure = (figure_id) => callApi('delete_facade_figure', {figure_id}) +const updateFacadeFigure = (figure_id, fields) => callApi('update_facade_figure', {figure_id, fields}) + +// Rooms +const updateRoom = (room) => callApi('update_room', {room}) +const saveRoom = (room) => callApi('save_room', {room}) +const get_room_entrance = (id) => callApi('get_room_entrance', {id}); +const deleteRoom = (room_id) => callApi('delete_room', {room_id}) +const copyRoom = (room_id, {floor_id, position, copies_count, floors_with_position }) => callApi('copy_room', {room_id, floor_id, position, copies_count, floors_with_position}) +const getRoomRequisition = (id) => callApi('get_room_requisition', {id}); + +// Room Plans +const createRoomPlan = (formData) => callWithFormData('add_plan_to_house', formData) +const connectRoomsToPlan = (room_ids, plan_id) => callApi('update_rooms_plan_link', {room_ids, plan_id}) +const sendRoomsToAdvert = (room_ids, house_id, type) => callApi('send_rooms_to_advert', {room_ids, house_id, type}) +const deleteRoomPlans = (plan_ids) => callApi('delete_house_plans', {plan_ids}) + +const getHousePlans = (house_id) => callApi('get_house_plans', {house_id}) + +const updateRoomPlans = (plan_ids, fields) => callApi('update_house_plans', {plan_ids, fields}) + +// Room Plan Photos + +const addPhotoRomRoomPlan = (formData) => callWithFormData('add_photos_to_room_plan', formData) +const updateRoomPlanPhotoPosition = (image_id, position) => callApi('update_plan_photo_position', {image_id, position}) +const deleteRoomPlanPhoto = (photo_id) => callApi('delete_photo_from_room_plan', photo_id) + +// Country +const getCountry = () => callApi('get_country') + +// Metro + +const getMetro = (region_id) => callApi('get_metro', {region_id}) + +// Regions + +const getRFRegions = () => callApi('get_rf_regions') + +// Decorations +const getDecorations = () => callApi('get_decorations') + +// Windows placements +const getWindowsPlacements = () => callApi('get_windows_placements') + +const deleteHouse = (house_id) => callApi('delete_house', { house_id }) + +const getComplexSuggestions = (use_empty_rooms = false) => callApi('get_complex_names_for_iframe', { use_empty_rooms }) + +const saveHouseUTP = (formData) => callWithFormData('save_house_utp', formData) + +const saveHouseDecoration = (formData) => callWithFormData('save_house_decoration', formData) + +const deleteHouseUtp = (id) => callApi('delete_house_utp', { id }) + +const deleteHouseDecoration = (id) => callApi('delete_house_decoration', { id }) + +const saveComplexAction = (formData) => callWithFormData('save_complex_action', formData) + +const deleteComplexAction = (id) => callApi('delete_complex_action', { id }) + +const getBuilders = () => callApi('get_builders'); + +const getAllHouses = () => callApi('get_all_houses') + +const createImport = (formData) => callWithFormData('create_import', formData); + +const getHouseImport = (import_id) => callApi('get_house_import', { import_id }) + +const getHouseImportStatus = (import_id) => callApi('get_house_import_status', { import_id }) + +const updateHouseImport = (import_id, fields) => callApi("update_house_import", { import_id, fields }) + +const getRoomFigures = (room_id) => callApi('get_room_figures', { room_id }) + +const getFilterFields = (house_id) => callApi("get_filter_fields", { house_id }) + +// Frame access + +const getFrameAccess = (token) => callApi("get_frame_access", { token }) + +// Frame colors + +const getAllColors = (token) => callApi("get_frame_colors", { token }) +// Create requisition +const createRequisition = (formData) => callWithFormData('create_requisition', formData) + +export default { + URL, + callApi, + getHouseEntrances, + getComplexEntrances, + createEntrance, + updateEntrance, + getComplexHouses, + getComplexOne, + searchComplexes, + deleteEntrance, + updateFloor, + getHouseOne, + getFloorPlan, + updateFloorPlans, + deleteFloorPlanFigure, + addFigureToFloorPlan, + updateFigure, + deleteHouseFacade, + addFacadeToHouse, + updateFacade, + getFacades, + connectRoomsToPlan, + sendRoomsToAdvert, + addPhotoToHouse, + deleteHousePhoto, + getHousePlans, + updateRoom, + deleteRoom, + addFigureToFacade, + deleteFacadeFigure, + updateFacadeFigure, + getComplexTypes, + getCountry, + getMetro, + getRFRegions, + saveComplex, + archiveComplex, + deleteComplex, + getComplex, + getComplexStatus, + getDecorations, + getWindowsPlacements, + getHouseTypes, + addEditHouse, + copyRoom, + createFloor, + deleteFloor, + copyFloor, + addFloorPlan, + getFloorPlans, + deleteFloorPlan, + getHouseFloorPlans, + saveRoom, + get_room_entrance, + getRoomRequisition, + updateFloorPlanPhoto, + updateComplexPhoto, + deleteComplexPhoto, + addPhotoToComplex, + createRoomPlan, + deleteRoomPlans, + updateRoomPlans, + addPhotoRomRoomPlan, + updateRoomPlanPhotoPosition, + deleteRoomPlanPhoto, + deleteHouse, + getComplexSuggestions, + saveHouseUTP, + saveHouseDecoration, + deleteHouseUtp, + deleteHouseDecoration, + saveComplexAction, + deleteComplexAction, + getBuilders, + getAllHouses, + createImport, + getHouseImport, + updateHouseImport, + getHouseImportStatus, + getRoomFigures, + getFilterFields, + getFrameAccess, + getAllColors, + createRequisition +} \ No newline at end of file diff --git a/src/shared/assets/fonts/Lato-Black.woff b/src/shared/assets/fonts/Lato-Black.woff new file mode 100644 index 0000000..cdd6b8d Binary files /dev/null and b/src/shared/assets/fonts/Lato-Black.woff differ diff --git a/src/shared/assets/fonts/Lato-Black.woff2 b/src/shared/assets/fonts/Lato-Black.woff2 new file mode 100644 index 0000000..44aa4c5 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Black.woff2 differ diff --git a/src/shared/assets/fonts/Lato-Bold.woff b/src/shared/assets/fonts/Lato-Bold.woff new file mode 100644 index 0000000..5f34582 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Bold.woff differ diff --git a/src/shared/assets/fonts/Lato-Bold.woff2 b/src/shared/assets/fonts/Lato-Bold.woff2 new file mode 100644 index 0000000..0a302fa Binary files /dev/null and b/src/shared/assets/fonts/Lato-Bold.woff2 differ diff --git a/src/shared/assets/fonts/Lato-ExtraBold.woff b/src/shared/assets/fonts/Lato-ExtraBold.woff new file mode 100644 index 0000000..2cc5e36 Binary files /dev/null and b/src/shared/assets/fonts/Lato-ExtraBold.woff differ diff --git a/src/shared/assets/fonts/Lato-ExtraBold.woff2 b/src/shared/assets/fonts/Lato-ExtraBold.woff2 new file mode 100644 index 0000000..6dc328a Binary files /dev/null and b/src/shared/assets/fonts/Lato-ExtraBold.woff2 differ diff --git a/src/shared/assets/fonts/Lato-ExtraLight.woff b/src/shared/assets/fonts/Lato-ExtraLight.woff new file mode 100644 index 0000000..7cb9f6f Binary files /dev/null and b/src/shared/assets/fonts/Lato-ExtraLight.woff differ diff --git a/src/shared/assets/fonts/Lato-ExtraLight.woff2 b/src/shared/assets/fonts/Lato-ExtraLight.woff2 new file mode 100644 index 0000000..d536a66 Binary files /dev/null and b/src/shared/assets/fonts/Lato-ExtraLight.woff2 differ diff --git a/src/shared/assets/fonts/Lato-Light.woff b/src/shared/assets/fonts/Lato-Light.woff new file mode 100644 index 0000000..f2328a3 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Light.woff differ diff --git a/src/shared/assets/fonts/Lato-Light.woff2 b/src/shared/assets/fonts/Lato-Light.woff2 new file mode 100644 index 0000000..f0b6529 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Light.woff2 differ diff --git a/src/shared/assets/fonts/Lato-Medium.woff b/src/shared/assets/fonts/Lato-Medium.woff new file mode 100644 index 0000000..a9f25b6 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Medium.woff differ diff --git a/src/shared/assets/fonts/Lato-Medium.woff2 b/src/shared/assets/fonts/Lato-Medium.woff2 new file mode 100644 index 0000000..327c89f Binary files /dev/null and b/src/shared/assets/fonts/Lato-Medium.woff2 differ diff --git a/src/shared/assets/fonts/Lato-Regular.woff b/src/shared/assets/fonts/Lato-Regular.woff new file mode 100644 index 0000000..1a4b8ae Binary files /dev/null and b/src/shared/assets/fonts/Lato-Regular.woff differ diff --git a/src/shared/assets/fonts/Lato-Regular.woff2 b/src/shared/assets/fonts/Lato-Regular.woff2 new file mode 100644 index 0000000..5dbd361 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Regular.woff2 differ diff --git a/src/shared/assets/fonts/Lato-SemiBold.woff b/src/shared/assets/fonts/Lato-SemiBold.woff new file mode 100644 index 0000000..6f295c5 Binary files /dev/null and b/src/shared/assets/fonts/Lato-SemiBold.woff differ diff --git a/src/shared/assets/fonts/Lato-SemiBold.woff2 b/src/shared/assets/fonts/Lato-SemiBold.woff2 new file mode 100644 index 0000000..fb458ea Binary files /dev/null and b/src/shared/assets/fonts/Lato-SemiBold.woff2 differ diff --git a/src/shared/assets/fonts/Lato-Thin.woff b/src/shared/assets/fonts/Lato-Thin.woff new file mode 100644 index 0000000..e7adba4 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Thin.woff differ diff --git a/src/shared/assets/fonts/Lato-Thin.woff2 b/src/shared/assets/fonts/Lato-Thin.woff2 new file mode 100644 index 0000000..de8e291 Binary files /dev/null and b/src/shared/assets/fonts/Lato-Thin.woff2 differ diff --git a/src/shared/assets/icons/archive.vue b/src/shared/assets/icons/archive.vue new file mode 100644 index 0000000..872489f --- /dev/null +++ b/src/shared/assets/icons/archive.vue @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/src/shared/assets/icons/block.vue b/src/shared/assets/icons/block.vue new file mode 100644 index 0000000..1f5e1d1 --- /dev/null +++ b/src/shared/assets/icons/block.vue @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/src/shared/assets/icons/homeNav/chess.svg b/src/shared/assets/icons/homeNav/chess.svg new file mode 100644 index 0000000..18686ed --- /dev/null +++ b/src/shared/assets/icons/homeNav/chess.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/homeNav/facades.svg b/src/shared/assets/icons/homeNav/facades.svg new file mode 100644 index 0000000..d4bd41b --- /dev/null +++ b/src/shared/assets/icons/homeNav/facades.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/homeNav/floors.svg b/src/shared/assets/icons/homeNav/floors.svg new file mode 100644 index 0000000..328d588 --- /dev/null +++ b/src/shared/assets/icons/homeNav/floors.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/homeNav/grid.svg b/src/shared/assets/icons/homeNav/grid.svg new file mode 100644 index 0000000..b7bd370 --- /dev/null +++ b/src/shared/assets/icons/homeNav/grid.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/house.vue b/src/shared/assets/icons/house.vue new file mode 100644 index 0000000..8689320 --- /dev/null +++ b/src/shared/assets/icons/house.vue @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/src/shared/assets/icons/iframeNavIcons/all-objects.svg b/src/shared/assets/icons/iframeNavIcons/all-objects.svg new file mode 100644 index 0000000..b3d77a1 --- /dev/null +++ b/src/shared/assets/icons/iframeNavIcons/all-objects.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/shared/assets/icons/iframeNavIcons/close-navbar.svg b/src/shared/assets/icons/iframeNavIcons/close-navbar.svg new file mode 100644 index 0000000..3162733 --- /dev/null +++ b/src/shared/assets/icons/iframeNavIcons/close-navbar.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/shared/assets/icons/iframeNavIcons/facades.svg b/src/shared/assets/icons/iframeNavIcons/facades.svg new file mode 100644 index 0000000..c52258b --- /dev/null +++ b/src/shared/assets/icons/iframeNavIcons/facades.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/iframeNavIcons/flat-planes.svg b/src/shared/assets/icons/iframeNavIcons/flat-planes.svg new file mode 100644 index 0000000..f7b19e4 --- /dev/null +++ b/src/shared/assets/icons/iframeNavIcons/flat-planes.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/iframeNavIcons/floors.svg b/src/shared/assets/icons/iframeNavIcons/floors.svg new file mode 100644 index 0000000..d992677 --- /dev/null +++ b/src/shared/assets/icons/iframeNavIcons/floors.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/iframeNavIcons/plate.svg b/src/shared/assets/icons/iframeNavIcons/plate.svg new file mode 100644 index 0000000..cd40f78 --- /dev/null +++ b/src/shared/assets/icons/iframeNavIcons/plate.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/iframeNavIcons/tile.svg b/src/shared/assets/icons/iframeNavIcons/tile.svg new file mode 100644 index 0000000..a10e742 --- /dev/null +++ b/src/shared/assets/icons/iframeNavIcons/tile.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/shared/assets/icons/metro.svg b/src/shared/assets/icons/metro.svg new file mode 100644 index 0000000..2d01d71 --- /dev/null +++ b/src/shared/assets/icons/metro.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/plus.vue b/src/shared/assets/icons/plus.vue new file mode 100644 index 0000000..c2bd5a3 --- /dev/null +++ b/src/shared/assets/icons/plus.vue @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/src/shared/assets/icons/search-arrow.vue b/src/shared/assets/icons/search-arrow.vue new file mode 100644 index 0000000..5b06c95 --- /dev/null +++ b/src/shared/assets/icons/search-arrow.vue @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/src/shared/assets/icons/typeNav/list.svg b/src/shared/assets/icons/typeNav/list.svg new file mode 100644 index 0000000..db2707b --- /dev/null +++ b/src/shared/assets/icons/typeNav/list.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/shared/assets/icons/typeNav/table.svg b/src/shared/assets/icons/typeNav/table.svg new file mode 100644 index 0000000..eb32303 --- /dev/null +++ b/src/shared/assets/icons/typeNav/table.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/shared/assets/icons/typeNav/tile.svg b/src/shared/assets/icons/typeNav/tile.svg new file mode 100644 index 0000000..cbd0b00 --- /dev/null +++ b/src/shared/assets/icons/typeNav/tile.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/shared/assets/icons/unarchive.vue b/src/shared/assets/icons/unarchive.vue new file mode 100644 index 0000000..933b135 --- /dev/null +++ b/src/shared/assets/icons/unarchive.vue @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/src/shared/assets/icons/viewGrid/facades.svg b/src/shared/assets/icons/viewGrid/facades.svg new file mode 100644 index 0000000..29c93cc --- /dev/null +++ b/src/shared/assets/icons/viewGrid/facades.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/viewGrid/facades.vue b/src/shared/assets/icons/viewGrid/facades.vue new file mode 100644 index 0000000..34daafe --- /dev/null +++ b/src/shared/assets/icons/viewGrid/facades.vue @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/src/shared/assets/icons/viewGrid/floors.svg b/src/shared/assets/icons/viewGrid/floors.svg new file mode 100644 index 0000000..47f3261 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/floors.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/viewGrid/floors.vue b/src/shared/assets/icons/viewGrid/floors.vue new file mode 100644 index 0000000..c777845 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/floors.vue @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/src/shared/assets/icons/viewGrid/grid.svg b/src/shared/assets/icons/viewGrid/grid.svg new file mode 100644 index 0000000..640f214 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/grid.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/shared/assets/icons/viewGrid/grid.vue b/src/shared/assets/icons/viewGrid/grid.vue new file mode 100644 index 0000000..03ce0d3 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/grid.vue @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/src/shared/assets/icons/viewGrid/layouts.svg b/src/shared/assets/icons/viewGrid/layouts.svg new file mode 100644 index 0000000..a284e39 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/layouts.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/viewGrid/layouts.vue b/src/shared/assets/icons/viewGrid/layouts.vue new file mode 100644 index 0000000..e4e8db5 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/layouts.vue @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/src/shared/assets/icons/viewGrid/list.svg b/src/shared/assets/icons/viewGrid/list.svg new file mode 100644 index 0000000..e4a3120 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/list.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/shared/assets/icons/viewGrid/list.vue b/src/shared/assets/icons/viewGrid/list.vue new file mode 100644 index 0000000..9ff8078 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/list.vue @@ -0,0 +1,31 @@ + + + \ No newline at end of file diff --git a/src/shared/assets/icons/viewGrid/tile.svg b/src/shared/assets/icons/viewGrid/tile.svg new file mode 100644 index 0000000..9ae1844 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/tile.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/icons/viewGrid/tile.vue b/src/shared/assets/icons/viewGrid/tile.vue new file mode 100644 index 0000000..8bd9d13 --- /dev/null +++ b/src/shared/assets/icons/viewGrid/tile.vue @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/src/shared/assets/icons/zoom.svg b/src/shared/assets/icons/zoom.svg new file mode 100644 index 0000000..d8c1101 --- /dev/null +++ b/src/shared/assets/icons/zoom.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/assets/images/complexes/close.svg b/src/shared/assets/images/complexes/close.svg new file mode 100644 index 0000000..2bcbfac --- /dev/null +++ b/src/shared/assets/images/complexes/close.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/shared/assets/images/complexes/complex1.jpg b/src/shared/assets/images/complexes/complex1.jpg new file mode 100644 index 0000000..d39fa2a Binary files /dev/null and b/src/shared/assets/images/complexes/complex1.jpg differ diff --git a/src/shared/assets/images/complexes/complex1.png b/src/shared/assets/images/complexes/complex1.png new file mode 100644 index 0000000..aafe1b6 Binary files /dev/null and b/src/shared/assets/images/complexes/complex1.png differ diff --git a/src/shared/assets/images/complexes/dropdown-icon.svg b/src/shared/assets/images/complexes/dropdown-icon.svg new file mode 100644 index 0000000..40c2a3f --- /dev/null +++ b/src/shared/assets/images/complexes/dropdown-icon.svg @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/src/shared/assets/images/complexes/edit.svg b/src/shared/assets/images/complexes/edit.svg new file mode 100644 index 0000000..ab3d10b --- /dev/null +++ b/src/shared/assets/images/complexes/edit.svg @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/src/shared/assets/images/complexes/gallery.svg b/src/shared/assets/images/complexes/gallery.svg new file mode 100644 index 0000000..9779b5a --- /dev/null +++ b/src/shared/assets/images/complexes/gallery.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/shared/assets/images/complexes/settings.svg b/src/shared/assets/images/complexes/settings.svg new file mode 100644 index 0000000..5f30343 --- /dev/null +++ b/src/shared/assets/images/complexes/settings.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/src/shared/assets/images/complexes/trash.svg b/src/shared/assets/images/complexes/trash.svg new file mode 100644 index 0000000..f7b435a --- /dev/null +++ b/src/shared/assets/images/complexes/trash.svg @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/src/shared/assets/images/facades/facade1.png b/src/shared/assets/images/facades/facade1.png new file mode 100644 index 0000000..0fcd108 Binary files /dev/null and b/src/shared/assets/images/facades/facade1.png differ diff --git a/src/shared/assets/images/floors/floor25.png b/src/shared/assets/images/floors/floor25.png new file mode 100644 index 0000000..699210e Binary files /dev/null and b/src/shared/assets/images/floors/floor25.png differ diff --git a/src/shared/assets/images/homes/home1.png b/src/shared/assets/images/homes/home1.png new file mode 100644 index 0000000..3c08829 Binary files /dev/null and b/src/shared/assets/images/homes/home1.png differ diff --git a/src/shared/assets/images/icons-bookmark-16-green.png b/src/shared/assets/images/icons-bookmark-16-green.png new file mode 100644 index 0000000..f812e13 Binary files /dev/null and b/src/shared/assets/images/icons-bookmark-16-green.png differ diff --git a/src/shared/assets/images/icons-bookmark-16-orange.png b/src/shared/assets/images/icons-bookmark-16-orange.png new file mode 100644 index 0000000..145726a Binary files /dev/null and b/src/shared/assets/images/icons-bookmark-16-orange.png differ diff --git a/src/shared/assets/images/icons-bookmark-16-red.png b/src/shared/assets/images/icons-bookmark-16-red.png new file mode 100644 index 0000000..151164e Binary files /dev/null and b/src/shared/assets/images/icons-bookmark-16-red.png differ diff --git a/src/shared/assets/images/icons-bookmark-22-green.png b/src/shared/assets/images/icons-bookmark-22-green.png new file mode 100644 index 0000000..135aa03 Binary files /dev/null and b/src/shared/assets/images/icons-bookmark-22-green.png differ diff --git a/src/shared/assets/images/icons-bookmark-22-orange.png b/src/shared/assets/images/icons-bookmark-22-orange.png new file mode 100644 index 0000000..dee4ca9 Binary files /dev/null and b/src/shared/assets/images/icons-bookmark-22-orange.png differ diff --git a/src/shared/assets/images/icons-bookmark-22-red.png b/src/shared/assets/images/icons-bookmark-22-red.png new file mode 100644 index 0000000..db9da83 Binary files /dev/null and b/src/shared/assets/images/icons-bookmark-22-red.png differ diff --git a/src/shared/assets/images/icons8-bookmark-22.png b/src/shared/assets/images/icons8-bookmark-22.png new file mode 100644 index 0000000..1cc9399 Binary files /dev/null and b/src/shared/assets/images/icons8-bookmark-22.png differ diff --git a/src/shared/assets/images/info.png b/src/shared/assets/images/info.png new file mode 100644 index 0000000..10bb29d Binary files /dev/null and b/src/shared/assets/images/info.png differ diff --git a/src/shared/assets/images/layoutsRoom/layout1.png b/src/shared/assets/images/layoutsRoom/layout1.png new file mode 100644 index 0000000..f78eb62 Binary files /dev/null and b/src/shared/assets/images/layoutsRoom/layout1.png differ diff --git a/src/shared/assets/images/layoutsRoom/layout3d.png b/src/shared/assets/images/layoutsRoom/layout3d.png new file mode 100644 index 0000000..ab4bb73 Binary files /dev/null and b/src/shared/assets/images/layoutsRoom/layout3d.png differ diff --git a/src/shared/assets/images/logo-company.png b/src/shared/assets/images/logo-company.png new file mode 100644 index 0000000..6b97d39 Binary files /dev/null and b/src/shared/assets/images/logo-company.png differ diff --git a/src/shared/assets/images/mark.png b/src/shared/assets/images/mark.png new file mode 100644 index 0000000..c82f965 Binary files /dev/null and b/src/shared/assets/images/mark.png differ diff --git a/src/shared/assets/images/rocket-spinner.svg b/src/shared/assets/images/rocket-spinner.svg new file mode 100644 index 0000000..913d1c8 --- /dev/null +++ b/src/shared/assets/images/rocket-spinner.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/shared/assets/images/said.png b/src/shared/assets/images/said.png new file mode 100644 index 0000000..442d9d9 Binary files /dev/null and b/src/shared/assets/images/said.png differ diff --git a/src/shared/assets/images/skyscraper.svg b/src/shared/assets/images/skyscraper.svg new file mode 100644 index 0000000..fc28009 --- /dev/null +++ b/src/shared/assets/images/skyscraper.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/shared/assets/images/test.vue b/src/shared/assets/images/test.vue new file mode 100644 index 0000000..e69de29 diff --git a/src/shared/assets/images/warning.png b/src/shared/assets/images/warning.png new file mode 100644 index 0000000..0f6ca60 Binary files /dev/null and b/src/shared/assets/images/warning.png differ diff --git a/src/shared/assets/images/ymarker.svg b/src/shared/assets/images/ymarker.svg new file mode 100644 index 0000000..75b59ad --- /dev/null +++ b/src/shared/assets/images/ymarker.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/layouts/AccessDenied.vue b/src/shared/layouts/AccessDenied.vue new file mode 100644 index 0000000..1735fea --- /dev/null +++ b/src/shared/layouts/AccessDenied.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/src/shared/layouts/ApartmentLayout.vue b/src/shared/layouts/ApartmentLayout.vue new file mode 100644 index 0000000..740b2ce --- /dev/null +++ b/src/shared/layouts/ApartmentLayout.vue @@ -0,0 +1,249 @@ + + + + + + diff --git a/src/shared/layouts/TestLayout.vue b/src/shared/layouts/TestLayout.vue new file mode 100644 index 0000000..608f38c --- /dev/null +++ b/src/shared/layouts/TestLayout.vue @@ -0,0 +1,64 @@ + + + + + \ No newline at end of file diff --git a/src/shared/layouts/ViewApartment.vue b/src/shared/layouts/ViewApartment.vue new file mode 100644 index 0000000..051b7e7 --- /dev/null +++ b/src/shared/layouts/ViewApartment.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/src/shared/layouts/ViewLayout.vue b/src/shared/layouts/ViewLayout.vue new file mode 100644 index 0000000..82b65d8 --- /dev/null +++ b/src/shared/layouts/ViewLayout.vue @@ -0,0 +1,133 @@ + + + + + + diff --git a/src/shared/layouts/defaultLayout.vue b/src/shared/layouts/defaultLayout.vue new file mode 100644 index 0000000..10f12c8 --- /dev/null +++ b/src/shared/layouts/defaultLayout.vue @@ -0,0 +1,232 @@ + + + + + + diff --git a/src/shared/utils/primeVueSettings.js b/src/shared/utils/primeVueSettings.js new file mode 100644 index 0000000..ed29e60 --- /dev/null +++ b/src/shared/utils/primeVueSettings.js @@ -0,0 +1,230 @@ +import { ButtonProps } from "primevue/button"; +import { CalendarPassThroughOptions } from "primevue/calendar"; +import { DropdownContext } from "primevue/dropdown"; +import { InputSwitchProps } from "primevue/inputswitch"; +import { InputTextContext } from "primevue/inputtext"; +import { MessageProps } from "primevue/message"; +import { SelectButtonContext } from "primevue/selectbutton"; +//import { PTOptions } from "primevue/ts-helpers"; +var main_color +const colors = JSON.parse(localStorage.getItem('colors')); +colors? main_color = colors.frame_color:setTimeout(() => { + window.location.reload() +}, 5000); + +export default { + pt: { + button: { + root: ({ props }) => { + return { + class: [ + "rounded ", + props?.severity === "success" + ? `!bg-${main_color} !text-white !shadow-none` + : props?.severity === "danger" + ? "!bg-red" + : props?.severity === "info" + ? `!border !border-${main_color}` + : props?.severity === "secondary" + ? "!bg-gradient-to-r !from-[#FEFEFE] !to-[#F6F6F6] !border !border-grey-400 !text-grey-900 !shadow-none" + : "", + props.link ? `!bg-transparent !text-${main_color}` : "", + props.text ? "!p-0" : "!py-2 !px-3", + ], + }; + }, + }, + inputText: { + root: ({ context }) => { + return { + class: [ + "border border-grey-400 rounded h-9 py-2 px-4", + `focus:!shadow-none focus:!border-${main_color} hover:!border-${main_color}`, + context.disabled + ? "focus:!border-grey-400 hover:!border-grey-400 bg-[#F2F3F3]" + : "", + ], + }; + }, + }, + InputMask: { + root: ({ context }) => { + return { + class: [ + "border border-grey-400 rounded h-9 py-2 px-4", + `focus:!shadow-none focus:!border-${main_color} hover:!border-${main_color}`, + context.disabled + ? "focus:!border-grey-400 hover:!border-grey-400 bg-[#F2F3F3]" + : "", + ], + }; + }, + }, + inputswitch: { + root: { class: ["inline-block relative", "w-12 h-7"] }, + slider: ({ props }) => { + return { + class: [ + "absolute cursor-pointer top-0 left-0 right-0 bottom-0 border border-transparent", + "transition-colors duration-200 rounded-2xl", + "focus:outline-none focus:outline-offset-0 focus:shadow-none", + "before:absolute before:content-'' before:top-1/2 before:bg-white before:dark:bg-gray-900 before:w-5 before:h-5 before:left-1 before:-mt-2.5 before:rounded-full before:transition-duration-200", + props.modelValue ? `!bg-${main_color}` : "!bg-grey-400", + ], + }; + }, + }, + dropdown: { + root: { + class: [ + "border border-grey-400 rounded h-9 !shadow-none", + `focus:!border-${main_color} focus:!shadow-none`, + `hover:!border-${main_color} active:!border-${main_color}`, + ], + }, + wrapper: { + class: "border border-grey-400 rounded", + }, + trigger: { + class: ["!w-max !ml-2"], + }, + item: ({ context }) => ({ + class: [ + "!text-sm !text-grey-900 !p-3 !justify-between ", + context.selected ? "!bg-stone-400" : "", + ], + }), + input: { + class: ["!p-0 !text-sm !flex !items-center"], + }, + emptyMessage: { + class: "text-grey-900 text-sm", + }, + }, + multiselect: { + root: { + class: [ + "border border-grey-400 rounded h-9 py-2 px-4 !shadow-none w-full", + `focus:!border-${main_color} focus:!shadow-none`, + `hover:!border-${main_color} active:!border-${main_color}`, + ], + }, + wrapper: { + class: "border border-grey-400 rounded px-4 py-2 bg-white w-full", + }, + trigger: { + class: ["w-full"], // Убираем !w-max, чтобы заняло всю ширину + }, + item: { + class: "text-grey-900", + }, + emptyMessage: { + class: "text-grey-900 text-sm", + }, + item: ({ context }) => ({ + class: [ + "!text-sm !text-grey-900 !p-3", + context.selected ? `!bg-${main_color}-50` : "", + ], + }), + input: { + class: ["!p-0 !text-sm !flex !items-center w-full truncate"], + }, + }, + textarea: { + root: { class: "border border-grey-400 rounded h-9 py-2 px-4" }, + }, + tabpanel: { + headerTitle: (instance) => ({ + class: [ + instance.tabpanel.context.active + ? `font-semibold text-${main_color}` + : "font-normal text-black", + ], + }), + headerAction: (instance) => ({ + class: [ + instance.tabpanel.context.active ? `!border-${main_color}` : "", + "focus:!shadow-none", + ], + }), + }, + tabview: { + panelContainer: { + class: ["!p-0 !mt-7.5"], + }, + }, + message: { + root: ({ props }) => { + return { + class: [ + "w-full !border !m-0", + props.severity === "info" + ? `!bg-${main_color}-100 !border !border-l !border-${main_color}-500` + : "", + ], + }; + }, + wrapper: () => { + return { + class: ["!p-3.5"], + }; + }, + icon: ({ props }) => { + return { + class: [props.severity === "info" ? "mr-4" : ""], + }; + }, + text: ({ props }) => { + return { + class: [ + props.severity === "info" ? "2xl:max-w-[1377px]" : "", + "text-black 2xl:!text-sm xl:!text-[13px]", + ], + }; + }, + }, + selectButton: { + root: () => ({ + class: ["flex flex-row !gap-x-2.5"], + }), + button: ({ context }) => ({ + class: [ + "!border-r !border-solid !rounded focus:!shadow-none h-full", + "2xl:py-2.5 2xl:px-2.5 xl:py-1.5 xl:px-2.5", + context.active ? `!bg-${main_color} !border-${main_color}` : "!border-grey-400", + ], + }), + label: ({ context }) => ({ + class: [ + "!text-grey-900 !text-sm", + "2xl: !text-sm xl: !text-sm", + context.active ? "!text-white !font-bold" : "!font-normal", + ], + }), + }, + + calendar: { + input: { + class: [ + "!border-r-0 !border-grey-400 hover:!border-grey-400 focus:!shadow-none h-9 !rounded-s-md !rounded-e-none", + ], + }, + timePicker: { + class: "bg-white text-grey-900 h-max", + }, + container: { + class: "h-max", + }, + dropdownButton: { + root: { + class: ["!bg-white !border-1 !border-l-0 !border-grey-400 h-9"], + }, + }, + hourPicker: {}, + separatorContainer: { + class: "mx-4", + }, + }, + }, +}; diff --git a/src/shared/utils/util.js b/src/shared/utils/util.js new file mode 100644 index 0000000..e75d4f1 --- /dev/null +++ b/src/shared/utils/util.js @@ -0,0 +1,683 @@ + + +export function isEmpty(data) { + if (typeof data === "object") { + if (JSON.stringify(data) === "{}" || JSON.stringify(data) === "[]") { + return true; + } else if (!data) { + return true; + } + return false; + } else if (typeof data === "string") { + if (!data.trim()) { + return true; + } + return false; + } else if (typeof data === "undefined") { + return true; + } else { + return false; + } +} + +export const layout_futures = { + none_of_the_above: "Ничего из перечисленного", + studio: "Студия", + european_layout: "Европланировка", + available: "Свободная", +} + +export const room_types = { + apartment: 'Квартира', + apartments: 'Апартаменты', + parking_space: 'Машиноместо', + office: 'Офис', + storage_room: 'Кладовка', + warehouse: 'Склад', + retail_space: 'Торговое Помещение', + commercial_space: 'Помещение свободного назначения', + townhouse: 'Таунхаус', + villa: 'Вилла', + penthouse: 'Пентхаус', + duplex: 'Дуплекс', + cottage: 'Коттедж', +}; + +export const room_types_which_has_rooms = [ + 'apartment', + 'apartments', + 'retail_space', + 'commercial_space', + 'townhouse', + 'villa', + 'penthouse', + 'duplex', + 'cottage' +] + +export const fixRoomVisible = (room, filters) => { + room.visible = true + const isMoreThenFive = filters.rooms.includes('5-more') + const isSelectedStudio = filters.rooms.includes('studio') + const isRoomCountSelected = filters.rooms.includes(String(room.rooms_count)); + + + if (filters.rooms.length) { + if (filters.selectedTypes.some((type) => room_types_which_has_rooms.includes(type))) { + if (isSelectedStudio) { + room.visible = room.layout_feature == 'studio' || ( + isRoomCountSelected && room.layout_feature != 'studio' || (isMoreThenFive && room.rooms_count >= 5) + ) + } else { + room.visible = (isRoomCountSelected && room.layout_feature != 'studio' || (isMoreThenFive && room.rooms_count >= 5)) + } + } + } + + if (filters.onlyFree && room.status_type !== 'available') { + room.visible = false + } + + if (filters.price.min !== '') { + if (!filters.useSquarePrice) { + if (filters.price.min > room.total_amount) { + room.visible = false + } + } else { + if (filters.price.min > room.total_amount / room.area) { + room.visible = false + } + } + } + + if (filters.price.max !== '' ) { + if (!filters.useSquarePrice) { + if (filters.price.max < room.total_amount) { + room.visible = false + } + } else { + if (filters.price.max < room.total_amount / room.area) { + room.visible = false + } + } + } + + if (filters.square.min !== '' && filters.square.min > room.area ) { + room.visible = false + } + + if (filters.square.max !== '' && filters.square.max < room.area ) { + room.visible = false + } + + if (filters.squareKitchen.max !== '' && (!room.kitchen_area ? true : filters.squareKitchen.max < +room.kitchen_area) ) { + room.visible = false + } + + if (filters.squareKitchen.min !== '' && (!room.kitchen_area ? true : filters.squareKitchen.min > +room.kitchen_area) ) { + room.visible = false + } + + if (filters.selectedTypes.length && !filters.selectedTypes.includes(room.type)) { + room.visible = false + } + + if (filters.floor.from !== '' && room.floor && parseInt(filters.floor.from) > room.floor ) { + room.visible = false + } + if (filters.floor.to !== '' && room.floor && parseInt(filters.floor.to) < room.floor ) { + room.visible = false + } + + if (filters.selectedDecorations && filters.selectedDecorations.length && !filters.selectedDecorations.map(num => parseInt(num) ).includes(room.decoration_id)) { + room.visible = false + } + + if (filters.selectedWindowsPlacements && filters.selectedWindowsPlacements.length && !filters.selectedWindowsPlacements.map(num => parseInt(num) ).includes(room.windows_placement_id)) { + room.visible = false + } + + if (filters.number && room.number != filters.number) { + room.visible = false + } + //если выбрана просто площадка + if (filters.adsTargets && filters.adsTargets.length && (!filters.adsStates || !filters.adsStates.length)) { + //если у нас выбран домклик + if (filters.adsTargets.map(num => parseInt(num) ).includes(4) && !room.add_to_domclick_feed) { + room.visible = false; + } + //если у нас выбран авито + if (filters.adsTargets.map(num => parseInt(num) ).includes(1) && !room.add_to_avito_feed) { + room.visible = false; + } + //если у нас выбран циан + if (filters.adsTargets.map(num => parseInt(num) ).includes(2) && !room.add_to_cian_feed) { + room.visible = false; + } + } + //если выбран просто статус + if (filters.adsStates && filters.adsStates.length && (!filters.adsTargets || !filters.adsTargets.length)) { + onlyStateCheck(room, filters); + } + //если выбрана и площадка и статус + if (filters.adsTargets && filters.adsTargets.length && filters.adsStates && filters.adsStates.length) { + //если у нас выбран домклик + if (filters.adsTargets.map(num => parseInt(num) ).includes(4)) { + domclickStateCheck(room, filters); + } + //если у нас выбран авито + if (filters.adsTargets.map(num => parseInt(num) ).includes(1)) { + avitoStateCheck(room, filters); + } + //если у нас выбран циан + if (filters.adsTargets.map(num => parseInt(num) ).includes(2)) { + cianStateCheck(room, filters); + } + } + } + + export const stateError = (room) => { + return (room.add_to_domclick_feed && room.ads_domclick_error) || + (room.add_to_avito_feed && room.ads_avito_error) || (room.add_to_cian_feed && room.ads_cian_error); + } + + export const stateWait = (room) => { + return (room.add_to_domclick_feed && !room.ads_domclick_error && !room.ads_domclick_url) || + (room.add_to_avito_feed && !room.ads_avito_error && !room.ads_avito_url) || + (room.add_to_cian_feed && !room.ads_cian_error && !room.ads_cian_url); + } + + export const onlyStateCheck = (room, filters) => { + //делаем наоборот, считаем, что изначально не видно + let visible = false; + + //если не размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(1) && + !room.add_to_domclick_feed && !room.add_to_avito_feed && !room.add_to_cian_feed) { + visible = true; + } + //если отправлен + if (filters.adsStates.map(num => parseInt(num) ).includes(2) && stateWait(room)) { + visible = true; + } + //если с ошибками + if (filters.adsStates.map(num => parseInt(num) ).includes(3) && + (room.ads_domclick_error || room.ads_avito_error || room.ads_cian_error)) { + visible = true; + } + //если размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(4) && + !stateWait(room) && !stateError(room) && + ((room.add_to_domclick_feed && room.ads_domclick_url) || (room.add_to_avito_feed && room.ads_avito_url) + || (room.add_to_cian_feed && room.ads_cian_url))) { + visible = true; + } + + if (!visible) { + room.visible = false; + } + } + + export const domclickStateCheck = (room, filters) => { + //делаем наоборот, считаем, что изначально не видно + let visible = false; + + //если не размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(1) && !room.add_to_domclick_feed) { + visible = true; + } + //если отправлен + if (filters.adsStates.map(num => parseInt(num) ).includes(2) && room.add_to_domclick_feed && !room.ads_domclick_error && !room.ads_domclick_url) { + visible = true; + } + //если с ошибками + if (filters.adsStates.map(num => parseInt(num) ).includes(3) && room.ads_domclick_error) { + visible = true; + } + //если размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(4) && room.add_to_domclick_feed && room.ads_domclick_url) { + visible = true; + } + + if (!visible) { + room.visible = false; + } + } + + export const avitoStateCheck = (room, filters) => { + //делаем наоборот, считаем, что изначально не видно + let visible = false; + + //если не размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(1) && !room.add_to_avito_feed) { + visible = true; + } + //если отправлен + if (filters.adsStates.map(num => parseInt(num) ).includes(2) && room.add_to_avito_feed && !room.ads_avito_error && !room.ads_avito_url) { + visible = true; + } + //если с ошибками + if (filters.adsStates.map(num => parseInt(num) ).includes(3) && room.ads_avito_error) { + visible = true; + } + //если размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(4) && room.add_to_avito_feed && room.ads_avito_url) { + visible = true; + } + + if (!visible) { + room.visible = false; + } + } + +export const cianStateCheck = (room, filters) => { + //делаем наоборот, считаем, что изначально не видно + let visible = false; + + //если не размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(1) && !room.add_to_cian_feed) { + visible = true; + } + //если отправлен + if (filters.adsStates.map(num => parseInt(num) ).includes(2) && room.add_to_cian_feed && !room.ads_cian_error && !room.ads_cian_url) { + visible = true; + } + //если с ошибками + if (filters.adsStates.map(num => parseInt(num) ).includes(3) && room.ads_cian_error) { + visible = true; + } + //если размещается + if (filters.adsStates.map(num => parseInt(num) ).includes(4) && room.add_to_cian_feed && room.ads_cian_url) { + visible = true; + } + + if (!visible) { + room.visible = false; + } +} + + export const typeRooms = [ + { + name: "Студия", + value: "studio", + }, + { + name: "1", + value: "1", + }, + { + name: "2", + value: "2", + }, + { + name: "3", + value: "3", + }, + { + name: "4", + value: "4", + }, + { + name: "5 и более", + value: "5-more", + }, + ]; + +export const adsTargets = [ + { + name: "Авито", + value: "1", + }, + { + name: "Циан", + value: "2", + }, + { + name: "Домклик", + value: "4", + } +]; + +export const adsStates = [ + { + name: "Не размещается", + value: "1", + }, + { + name: "Отправлен", + value: "2", + }, + { + name: "С ошибками", + value: "3", + }, + { + name: "Размещается", + value: "4", + } +]; + + export const containsNumber = (str) => { + let regex = /\d/; + + return regex.test(str); + } + + export const isErrorStatus = (apartament) => { + return apartament.ads_domclick_error || apartament.ads_avito_error || apartament.ads_cian_error; + } + + export const isWaitStatus = (apartament) => { + let waitSt = false; + + if (apartament.add_to_domclick_feed && !apartament.ads_domclick_error && !apartament.ads_domclick_url) { + waitSt = true; + } + + if (apartament.add_to_avito_feed && !apartament.ads_avito_error && !apartament.ads_avito_url) { + waitSt = true; + } + + if (apartament.add_to_cian_feed && !apartament.ads_cian_error && !apartament.ads_cian_url) { + waitSt = true; + } + + return waitSt; + } + + export const isSendToAds = (apartament) => { + return apartament.add_to_cian_feed || apartament.add_to_avito_feed || apartament.add_to_domclick_feed || apartament.add_to_yandex_feed; + } + + export const isActiveStatus = (apartament) => { + let activeSt = false; + + if (!isWaitStatus(apartament) && !isErrorStatus(apartament)) { + if (apartament.add_to_domclick_feed && !apartament.ads_domclick_error && apartament.ads_domclick_url) { + activeSt = true; + } + + if (apartament.add_to_avito_feed && !apartament.ads_avito_error && apartament.ads_avito_url) { + activeSt = true; + } + + if (apartament.add_to_cian_feed && !apartament.ads_cian_error && apartament.ads_cian_url) { + activeSt = true; + } + } + + return activeSt; + } + + export const getStatusText = (apartament) => { + let statusText = ""; + + if (apartament.ads_domclick_error) { + statusText = statusText + `
Домклик - ошибка размещения

Ошибки размещения

` + apartament.ads_domclick_error + `
`; + } + + if (apartament.ads_avito_error) { + statusText = statusText + `
Авито - ошибка размещения

Ошибки размещения

` + apartament.ads_avito_error + `
`; + } + + if (apartament.ads_cian_error) { + statusText = statusText + `
Циан - ошибка размещения

Ошибки размещения

` + apartament.ads_cian_error + `
`; + } + + if (apartament.add_to_domclick_feed && !apartament.ads_domclick_error && !apartament.ads_domclick_url) { + statusText = statusText + `
Домклик - отправлен
`; + } + + if (apartament.add_to_avito_feed && !apartament.ads_avito_error && !apartament.ads_avito_url) { + statusText = statusText + `
Авито - отправлен
`; + } + + if (apartament.add_to_cian_feed && !apartament.ads_cian_error && !apartament.ads_cian_url) { + statusText = statusText + `
Циан - отправлен
`; + } + + if (apartament.add_to_domclick_feed && !apartament.ads_domclick_error && apartament.ads_domclick_url) { + statusText = statusText + `
Домклик - размещено
`; + } + + if (apartament.add_to_avito_feed && !apartament.ads_avito_error && apartament.ads_avito_url) { + statusText = statusText + `
Авито - размещено
`; + } + + if (apartament.add_to_cian_feed && !apartament.ads_cian_error && apartament.ads_cian_url) { + statusText = statusText + `
Циан - размещено
`; + } + + return statusText; + } + + + export const formatNumber = (number) => { + if (!number) return 0; + + let numberStr = number.toString(); + + let parts = numberStr.split('.'); + + let integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ' '); + + let formattedNumber = parts.length === 2 ? integerPart + '.' + parts[1] : integerPart; + + return formattedNumber; + } + + export const join = (arr /*, separator */) => { + var separator = arguments.length > 1 ? arguments[1] : ", "; + return arr.filter(function(n){ + return n + }).join(separator); + } + +export const getConstraints = (kladr, label) => { + let ids = new Array(); + if (kladr == 0 && label == 'москва и московская область') { + ids.push({ kladr_id: "7700000000000" }); + ids.push({ kladr_id: "5000000000000" }); + } else if (kladr == 0 && label == 'санкт-петербург и ленинградская обл') { + ids.push({ kladr_id: "7800000000000" }); + ids.push({ kladr_id: "4700000000000" }); + } else if (kladr == 0 && label == 'крым и севастополь') { + ids.push({ kladr_id: "9100000000000" }); + ids.push({ kladr_id: "9200000000000" }); + } else { + ids.push({ kladr_id: kladr.toString() }); + } + + return ids; +} + +export const INITIAL_COMPLEX = { + id: 0, + type: 'complex', + name: '', + address: { + "id": null, + "complex_id": null, + "address": "", + // "rayon_id": null, + "rayon": null, + "city": "", + "street": "", + "korpus": "", + "dom": "", + "litera": "", + "kladr_id": "", + "latitude": "", + "longitude": "" + }, + actions: [], + address_line: '', + rf_region_id: 0, + country_id: 171, + rf_region_rayon: '', + // rayon_id: 0, + city: '', + street: '', + korpus: '', + dom: '', + litera: '', + kladr_id: '', + region_kladr_id: '', + latitude: '', + longitude: '', + lines: [], + deadline: [], + builder_id: null, + images: [], + payment_types: [], + contract_type: '', + housing_class_id: null, + first_floor: '', + last_floor: '', + number_apartments: '', + ceiling_height: '', + decoration_id: null, + parking: 0, + closed_area: 0, + protected_area: 0, + concierge: 0, + house_type: null, + house_material: null, + metros: [], + infrastructure: { + 'school': 0, + 'kindergarten': 0, + 'playgrounds': 0, + 'sportsgrounds': 0, + 'cafes': 0, + 'shops': 0, + 'beautysalons': 0, + 'fitnessclub': 0, + 'park': 0, + 'trc': 0, + 'commercialobjects': 0, + }, + metro: { + 'metro_id': null, + 'subway_time': '', + 'subway_distance': null, + }, + dop: { + domclick_id: null, + title_desc: '', + text_desc: '', + title_desc_dop: '', + text_desc_dop: '', + url_video: '', + type_video: null, + UTP: [], + UTP2: [], + phone_op: '', + phone_mobil_op: null, + address_op: '', + latitude_op: '', + longitude_op: '', + timezone_op: '', + workdays_op: null, + opentime_op: '', + closetime_op: '' + }, + cadastral_number: null, + has_show_room: false +} + +export const confirmpopupStyle = { + root: { + class: ["!p-3.5"], + }, + content: { + class: ["!p-0 !mb-2.5"], + }, + message: { + class: ["!m-0 !text-base !font-normal"], + }, + footer: { + class: ["!flex !flex-row-reverse !justify-center !gap-x-3 !p-0"], + }, + rejectButton: { + class: ["!px-3 !font-normal !m-0"], + }, + acceptButton: { + class: ["!px-3 !font-normal"], + }, +}; + +export const photoToObjectUrl = (url) => URL.createObjectURL(url) + +export const copyObject = (object) => JSON.parse(JSON.stringify(object)) + +export const generateAlphabetSequence = (number) => { + let result = []; + + function numberToAlphabet(n) { + let chars = []; + while (n > 0) { + let remainder = (n - 1) % 26; + chars.push(String.fromCharCode(remainder + 65)); + n = Math.floor((n - 1) / 26); + } + return chars.reverse().join(''); + } + + for (let i = 1; i <= number; i++) { + result.push(numberToAlphabet(i)); + } + + return result; +} + +export const multiSelectStyle = { + root: { + class: [ + '!rounded !border !border-grey-400 !p-3 !h-max', + ], + }, + input: { + class: [ + '!flex !flex-row !items-center !p-0 !text-sm !leading-none !text-grey-900 !mr-3', + ], + }, + wrapper: { + class: ['!p-0'], + }, + item: { + class: ['!text-sm'], + }, + label: { + class: ['!p-0'] + }, + header: { + class: ['custom-header'] + } +}; + + +export const formatDate = (utcDateStr) => { + const [datePart, timePart] = utcDateStr.split(' '); + const [year, month, day] = datePart.split('-').map(Number); + const [hour, minute, second] = timePart.split(':').map(Number); + + const utcDate = new Date(Date.UTC(year, month - 1, day, hour, minute, second)); + + const options = { + timeZone: 'Asia/Yerevan', + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false // 24-hour format + }; + + const formatter = new Intl.DateTimeFormat('en-US', options); + + const timeZonedDate = formatter.format(utcDate); + + return timeZonedDate +} \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..7f5b89c --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,14 @@ +import { createStore } from 'vuex' + +export default createStore({ + state: { + }, + getters: { + }, + mutations: { + }, + actions: { + }, + modules: { + } +}) diff --git a/src/widgets/AccordionFloors/AccordionFloorItem.vue b/src/widgets/AccordionFloors/AccordionFloorItem.vue new file mode 100644 index 0000000..b487def --- /dev/null +++ b/src/widgets/AccordionFloors/AccordionFloorItem.vue @@ -0,0 +1,527 @@ + + + + + diff --git a/src/widgets/AccordionFloors/AccordionFloors.css b/src/widgets/AccordionFloors/AccordionFloors.css new file mode 100644 index 0000000..1c32cd7 --- /dev/null +++ b/src/widgets/AccordionFloors/AccordionFloors.css @@ -0,0 +1,156 @@ +:root { + /* dark shades of primary color*/ + --clr-primary-1: hsl(205, 86%, 17%); + --clr-primary-2: hsl(205, 77%, 27%); + --clr-primary-3: hsl(205, 72%, 37%); + --clr-primary-4: hsl(205, 63%, 48%); + /* primary/main color */ + --clr-primary-5: hsl(205, 78%, 60%); + /* lighter shades of primary color */ + --clr-primary-6: hsl(205, 89%, 70%); + --clr-primary-7: hsl(205, 90%, 76%); + --clr-primary-8: hsl(205, 86%, 81%); + --clr-primary-9: hsl(205, 90%, 88%); + --clr-primary-10: hsl(205, 100%, 96%); + /* darkest grey - used for headings */ + --clr-grey-1: hsl(209, 61%, 16%); + --clr-grey-2: hsl(211, 39%, 23%); + --clr-grey-3: hsl(209, 34%, 30%); + --clr-grey-4: hsl(209, 28%, 39%); + /* grey used for paragraphs */ + --clr-grey-5: hsl(210, 22%, 49%); + --clr-grey-6: hsl(209, 23%, 60%); + --clr-grey-7: hsl(211, 27%, 70%); + --clr-grey-8: hsl(210, 31%, 80%); + --clr-grey-9: hsl(212, 33%, 89%); + --clr-grey-10: hsl(210, 36%, 96%); + --clr-white: #fff; + --clr-purple: #4b145b; + --clr-red-special: #b4345c; + --clr-grey-special: #eae6eb; + --clr-red-dark: hsl(360, 67%, 44%); + --clr-red-light: hsl(360, 71%, 66%); + --clr-green-dark: hsl(125, 67%, 44%); + --clr-green-light: hsl(125, 71%, 66%); + --clr-black: #222; + --transition: all 0.3s linear; + --spacing: 0.1rem; + --radius: 4px; + --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + --max-width: 1170px; + --fixed-width: 920px; + } + + *, + ::after, + ::before { + margin: 0; + padding: 0; + box-sizing: border-box; + } + body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, + Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; + background: var(--clr-purple); + color: var(--clr-grey-1); + line-height: 1.5; + font-size: 0.875rem; + } + ul { + list-style-type: none; + } + a { + text-decoration: none; + } + h1, + h2, + h3, + h4 { + letter-spacing: var(--spacing); + text-transform: capitalize; + line-height: 1.25; + margin-bottom: 0.75rem; + } + h1 { + font-size: 3rem; + } + h2 { + font-size: 2rem; + } + h3 { + font-size: 1.25rem; + } + h4 { + font-size: 0.875rem; + } + p { + margin-bottom: 1.25rem; + color: var(--clr-grey-5); + } + @media screen and (min-width: 800px) { + h1 { + font-size: 4rem; + } + h2 { + font-size: 2.5rem; + } + h3 { + font-size: 1.75rem; + } + h4 { + font-size: 1rem; + } + body { + font-size: 1rem; + } + h1, + h2, + h3, + h4 { + line-height: 1; + } + } + /* global classes */ + + main { + /* min-height: 100vh; */ + /* using flex because of better browser support */ + display: flex; + justify-content: center; + align-items: center; + } + + .container { + width: 90vw; + margin: 5rem auto; + background: var(--clr-white); + border-radius: var(--radius); + padding: 2.5rem 2rem; + max-width: var(--fixed-width); + display: grid; + gap: 1rem 2rem; + } + .container h3 { + line-height: 1.2; + font-weight: 500; + } + @media screen and (min-width: 992px) { + .container { + display: grid; + grid-template-columns: 250px 1fr; + } + } + + .section { + width: 90vw; + margin: 0 auto; + max-width: var(--max-width); + } + + @media screen and (min-width: 992px) { + .section { + width: 95vw; + } + } + \ No newline at end of file diff --git a/src/widgets/AccordionFloors/AccordionFloors.vue b/src/widgets/AccordionFloors/AccordionFloors.vue new file mode 100644 index 0000000..6d1f9c8 --- /dev/null +++ b/src/widgets/AccordionFloors/AccordionFloors.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/src/widgets/ArchivedListItems.vue b/src/widgets/ArchivedListItems.vue new file mode 100644 index 0000000..348a7e6 --- /dev/null +++ b/src/widgets/ArchivedListItems.vue @@ -0,0 +1,361 @@ + + + + + + + \ No newline at end of file diff --git a/src/widgets/Cropper.vue b/src/widgets/Cropper.vue new file mode 100644 index 0000000..a1a5a70 --- /dev/null +++ b/src/widgets/Cropper.vue @@ -0,0 +1,375 @@ + + + + + + \ No newline at end of file diff --git a/src/widgets/ListItems.vue b/src/widgets/ListItems.vue new file mode 100644 index 0000000..02528a7 --- /dev/null +++ b/src/widgets/ListItems.vue @@ -0,0 +1,705 @@ + + + + + + + diff --git a/src/widgets/ListItemsIframe.vue b/src/widgets/ListItemsIframe.vue new file mode 100644 index 0000000..3bf24a9 --- /dev/null +++ b/src/widgets/ListItemsIframe.vue @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/src/widgets/editFacades/popupWindow.vue b/src/widgets/editFacades/popupWindow.vue new file mode 100644 index 0000000..076c9f8 --- /dev/null +++ b/src/widgets/editFacades/popupWindow.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/src/widgets/editFacades/windowEditFacades.vue b/src/widgets/editFacades/windowEditFacades.vue new file mode 100644 index 0000000..e2ce620 --- /dev/null +++ b/src/widgets/editFacades/windowEditFacades.vue @@ -0,0 +1,584 @@ + + + + + + + + diff --git a/src/widgets/editPlanFloor/canvasMarkupFloor.vue b/src/widgets/editPlanFloor/canvasMarkupFloor.vue new file mode 100644 index 0000000..589fefd --- /dev/null +++ b/src/widgets/editPlanFloor/canvasMarkupFloor.vue @@ -0,0 +1,291 @@ + + + + + diff --git a/src/widgets/editPlanFloor/connectionWithLayout.vue b/src/widgets/editPlanFloor/connectionWithLayout.vue new file mode 100644 index 0000000..e8fb787 --- /dev/null +++ b/src/widgets/editPlanFloor/connectionWithLayout.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/src/widgets/editPlanFloor/markupFloor.vue b/src/widgets/editPlanFloor/markupFloor.vue new file mode 100644 index 0000000..935e609 --- /dev/null +++ b/src/widgets/editPlanFloor/markupFloor.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/src/widgets/editPlanFloor/useCanvas.js b/src/widgets/editPlanFloor/useCanvas.js new file mode 100644 index 0000000..2d392c7 --- /dev/null +++ b/src/widgets/editPlanFloor/useCanvas.js @@ -0,0 +1,327 @@ +import bus from "@/eventBus"; +import { onUpdated, ref, watch } from "vue"; + +/*type ImageSize = { + width: number; + height: number; +}; + +type Point = { + x: number; + y: number; +}; + +type Figure = { + readonly id: string; + points: Point[]; + background: string; +}; + +export type TContextMenuOptions = { + isVisible: boolean; + x: number; + y: number; +};*/ + +export default function useCanvas() { + const canvas = ref(null); + const ctx = ref(null); + const sourceImage = ref(null); + const imageSize = ref(null); + const figures = ref([]); + const currentFigure = ref(null); + const currentHoveredFigure = ref(null); + const idFigureOnContextMenu = ref(''); + const contextMenuOptions = ref({ + isVisible: false, + figure: null, + x: 0, + y: 0, + }); + + + function clickHandler(e) { + canvas.value = e.target; + ctx.value = canvas.value.getContext("2d"); + if (!sourceImage.value || !ctx.value || !canvas.value) return; + + if (contextMenuOptions.value.isVisible) { + contextMenuOptions.value.isVisible = false; + return; + } + + const x = e.clientX - canvas.value.getBoundingClientRect().left; + const y = e.clientY - canvas.value.getBoundingClientRect().top; + + if (currentFigure.value === null) { + currentFigure.value = []; + } + + if (currentFigure.value !== null) { + currentFigure.value.push({ x, y }); + + if (isClosedFigure(currentFigure.value)) { + bus.$emit('canvas:figure-added', { + points: currentFigure.value, + background: '173, 169, 219', + }) + currentFigure.value = null; + } + } + + redraw(); + } + + function contextmenuHandler(e) { + canvas.value = e.target + ctx.value = canvas.value.getContext("2d"); + e.preventDefault(); + if (!sourceImage.value || !ctx.value || !canvas.value) return; + + if (currentFigure.value && currentFigure.value.length > 0) { + currentFigure.value.pop(); + redraw(); + } else { + const x = e.clientX - canvas.value.getBoundingClientRect().left; + const y = e.clientY - canvas.value.getBoundingClientRect().top; + for (const figure of figures.value) { + const path = createFigurePath(figure.points); + if (ctx.value.isPointInPath(path, x, y)) { + idFigureOnContextMenu.value = figure.id; + contextMenuOptions.value.isVisible = true + contextMenuOptions.value.figure = figure + contextMenuOptions.value.x = x + contextMenuOptions.value.y = y + } + } + + return contextMenuOptions.value + } + + return null + } + + function mousemoveHandler(e) { + canvas.value = e.target + ctx.value = canvas.value.getContext("2d"); + if (!sourceImage.value || !ctx.value || !canvas.value) return; + + const x = e.clientX - canvas.value.getBoundingClientRect().left; + const y = e.clientY - canvas.value.getBoundingClientRect().top; + + for (const figure of figures.value) { + const path = createFigurePath(figure.points); + if (ctx.value.isPointInPath(path, x, y)) { + currentHoveredFigure.value = figure.id; + redraw(true, figure.id); + return; + } + } + + currentHoveredFigure.value = null; + redraw(); + } + + function isClosedFigure(points) { + if (points.length >= 3) { + const firstPoint = points[0]; + const lastPoint = points[points.length - 1]; + const distanceX = Math.abs(firstPoint.x - lastPoint.x); + const distanceY = Math.abs(firstPoint.y - lastPoint.y); + return distanceX < 10 && distanceY < 10; // Check if the last point is close to the first point + } + return false; + } + + function createFigurePath(points) { + if (!points) return; + + const path = new Path2D(); + path.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + path.lineTo(points[i].x, points[i].y); + } + path.closePath(); + return path; + } + + function calculateCenter(points) { + if (points.length === 0) { + return { x: 0, y: 0 }; + } + + let centerX = 0; + let centerY = 0; + for (const point of points) { + centerX += point.x; + centerY += point.y; + } + centerX /= points.length; + centerY /= points.length; + + return { x: centerX, y: centerY }; + } + + function deleteFigure(id) { + const index = figures.value.findIndex( + (el) => el.id === id + ); + if (index >= 0) figures.value.splice(index, 1); + + localStorage.setItem("figures", JSON.stringify(figures.value)); + + redraw(); + } + + watch(sourceImage, () => redraw()) + + const uploadImage = (file, canvasEl) => { + canvas.value = canvasEl; + ctx.value = canvas.value.getContext("2d"); + imageSize.value = localStorage.getItem("imageSize") + ? JSON.parse(localStorage.getItem("imageSize")) + : null; + figures.value = localStorage.getItem("figures") + ? JSON.parse(localStorage.getItem("figures")) + : []; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target) { + const image = new Image(); + image.onload = () => { + sourceImage.value = image; + if (imageSize.value) { + if ( + image.naturalWidth !== imageSize.value.width || + image.naturalHeight !== imageSize.value.height + ) { + localStorage.removeItem("figures"); + figures.value = []; + } + } + localStorage.setItem( + "imageSize", + JSON.stringify({ + width: image.naturalWidth, + height: image.naturalHeight, + }) + ); + redraw(); + }; + image.src = e.target.result; + } + }; + reader.readAsDataURL(file); + } + }; + + // onMounted(() => { + // window.addEventListener('resize', (e) => { + // if (canvas.value) { + // canvas.value.width = window.innerWidth + // redraw() + // } + // }) + // }) + function redraw(isHover, figureId) { + + console.log('redrawing'); + + if (!ctx.value || !sourceImage.value || !canvas.value) return; + canvas.value.width = sourceImage.value.naturalWidth; + canvas.value.height = sourceImage.value.naturalHeight; + ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height); + // Draw the image + ctx.value.drawImage( + sourceImage.value, + 0, + 0, + sourceImage.value.naturalWidth, + sourceImage.value.naturalHeight + ); + for (const figure of figures.value) { + ctx.value.fillStyle = `rgba(${figure.background}, ${isHover && figureId == figure.id ? '0.8' : '0.5'})`; + ctx.value.fill(createFigurePath(figure.points)); + } + if (currentFigure.value !== null) { + ctx.value.fillStyle = "red"; + ctx.value.strokeStyle = "blue"; + for (let i = 0; i < currentFigure.value.length; i++) { + const point = currentFigure.value[i]; + ctx.value.beginPath(); + ctx.value.arc(point.x, point.y, 5, 0, Math.PI * 2); + ctx.value.fill(); + if (i > 0) { + const prevPoint = currentFigure.value[i - 1]; + ctx.value.moveTo(prevPoint.x, prevPoint.y); + ctx.value.lineTo(point.x, point.y); + ctx.value.stroke(); + } + } + } + } + + function getRandomColor() { + const usedColors = new Set(figures.value.map((figure) => figure.background)); + const availableColors = [ + "#FF5733", + "#33FF57", + "#5733FF", + "#33FFFF", + "#FFFF33", + "#FF33FF", + ]; + const unusedColors = availableColors.filter( + (color) => !usedColors.has(hexToRgb(color)) + ); + if (unusedColors.length > 0) { + const randomIndex = Math.floor(Math.random() * unusedColors.length); + return unusedColors[randomIndex].startsWith("#") + ? hexToRgb(unusedColors[randomIndex]) + : unusedColors[randomIndex]; + } else { + // Если все цвета использованы, генерируем случайный цвет + const randomColor = `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor( + Math.random() * 256 + )}, ${Math.floor(Math.random() * 256)})`; + return randomColor.startsWith("#") + ? hexToRgb(randomColor) + : hexToRgb(randomColor); + } + } + + function hexToRgb(hex) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + const objColor = result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; + + return objColor ? `${objColor.r},${objColor.g},${objColor.b}` : null; + } + + function randomId(length = 6) { + return Math.random() + .toString(36) + .substring(2, length + 2); + } + + return { + figures, + sourceImage, + idFigureOnContextMenu, + contextMenuOptions, + clickHandler, + contextmenuHandler, + mousemoveHandler, + uploadImage, + deleteFigure, + canvas, + imageSize, + ctx + }; +} diff --git a/src/widgets/forms/complexAction.vue b/src/widgets/forms/complexAction.vue new file mode 100644 index 0000000..2f6dd46 --- /dev/null +++ b/src/widgets/forms/complexAction.vue @@ -0,0 +1,242 @@ +