123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- <template>
- <Dialog ref="dialog" v-model="dialogVisible">
- <template #header>
- <div class="row items-center">
- <div style="font-size: 110%">
- Расширенный поиск
- </div>
- <DivBtn class="q-ml-sm text-grey-5 bg-yellow-1" :size="28" :icon-size="24" icon="la la-question" round @click.stop.prevent="showSearchHelp">
- <template #tooltip>
- <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
- Памятка
- </q-tooltip>
- </template>
- </DivBtn>
- </div>
- </template>
- <div ref="box" class="column q-mt-xs overflow-auto" style="max-width: 660px; padding: 0px 10px 10px 10px;">
- <div class="row">
- <div v-for="f in recStruct" :key="f.field" class="row">
- <div class="q-mx-xs" />
- <q-input
- v-model="search[f.field]" :maxlength="5000"
- class="q-mt-xs" style="width: 150px;" :label="`(${f.type}) ${f.field}`"
- :bg-color="bgColor[f.field] || 'white'"
- stack-label outlined dense clearable
- @keydown="onKeyDown"
- >
- <q-tooltip v-if="search[f.field]" :delay="500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
- {{ search[f.field] }}
- </q-tooltip>
- </q-input>
- </div>
- </div>
- <div class="row q-mt-xs q-ml-sm" style="color: red" v-html="error" />
- </div>
- <template #footer>
- <q-btn class="q-px-md q-ml-sm" color="primary" dense no-caps :disabled="error !== ''" @click="apply">
- Применить
- </q-btn>
- </template>
- </Dialog>
- </template>
- <script>
- //-----------------------------------------------------------------------------
- import vueComponent from '../../vueComponent.js';
- import Dialog from '../../share/Dialog.vue';
- import DivBtn from '../../share/DivBtn.vue';
- import _ from 'lodash';
- const componentOptions = {
- components: {
- Dialog,
- DivBtn,
- },
- watch: {
- modelValue(newValue) {
- this.dialogVisible = newValue;
- },
- dialogVisible(newValue) {
- this.$emit('update:modelValue', newValue);
- },
- extSearch: {
- handler(newValue) {
- this.search = _.cloneDeep(newValue);
- },
- deep: true,
- },
- search: {
- handler() {
- this.validate();
- },
- deep: true,
- },
- }
- };
- class SelectExtSearchDialog {
- _options = componentOptions;
- _props = {
- modelValue: Boolean,
- extSearch: Object,
- };
- dialogVisible = false;
- search = {};
- bgColor = {};
- error = '';
- created() {
- this.commit = this.$store.commit;
- }
- mounted() {
- }
- get config() {
- return this.$store.state.config;
- }
- get recStruct() {
- if (this.config.dbConfig && this.config.dbConfig.inpxInfo.recStruct)
- return this.config.dbConfig.inpxInfo.recStruct;
- else
- return [];
- }
- validate() {
- const validNumValue = (n) => {
- const validChars = new Set('0123456789.'.split(''));
- for (const c of n.split(''))
- if (!validChars.has(c))
- return false;
- const v = n.split('..');
- if ( isNaN(parseInt(v[0] || '0', 10)) || isNaN(parseInt(v[1] || '0', 10)) )
- return false;
- return true;
- };
- let error = [];
- const s = this.search;
- for (const f of this.recStruct) {
- if (f.type == 'N' && s[f.field] && !validNumValue(s[f.field])) {
- error.push(`Недопустимое значение поля ${f.field}`);
- this.bgColor[f.field] = 'red-2';
- } else {
- this.bgColor[f.field] = '';//default
- }
- }
- this.error = error.join('<br>');
- }
- showSearchHelp() {
- let info = `<div style="min-width: 250px" />`;
- info += `
- <p>
- Расширенный поиск ведется непосредственно по значениям атрибутов записей описания книг.
- Атрибуты можно увидеть, если включить опцию "Показывать JSON".
- Названия атрибутов (кроме "_uid" и "id") соответствуют названиям полей струкутры записей из inpx-файла.
- На поисковые значения действуют те же правила, что и для разделов "Авторы", "Серии", "Книги".
- <br><br>
- Для строковых значений (S):
- <ul>
- <li>
- без префикса: значение трактуется, как "начинается с"
- </li>
- <li>
- префикс "=": поиск по точному совпадению
- </li>
- <li>
- префикс "*": поиск подстроки в строке
- </li>
- <li>
- префикс "#": поиск подстроки в строке, но только среди начинающихся не с латинского или кириллического символа
- </li>
- <li>
- префикс "~": поиск по регулярному выражению
- </li>
- <li>
- префикс "?": поиск пустых значений или тех, что начинаются с этого символа
- </li>
- </ul>
- Для числовых значений (N):
- <ul>
- <li>
- число N: поиск по точному совпадению
- </li>
- <li>
- диапазон N..M: поиск по диапазону числовых значений, включая N и M. Например, поисковое значение 1024..2048 в поле "size"
- найдет все ссылки на файлы размером от 1КБ до 2КБ.
- </li>
- </ul>
- </p>
- `;
- this.$root.stdDialog.alert(info, 'Памятка', {iconName: 'la la-info-circle'});
- }
- onKeyDown(event) {
- if (event.code == 'Enter')
- this.apply();
- }
- apply() {
- this.validate();
- if (!this.error) {
- this.$emit('update:extSearch', _.cloneDeep(this.search));
- this.dialogVisible = false;
- }
- }
- }
- export default vueComponent(SelectExtSearchDialog);
- //-----------------------------------------------------------------------------
- </script>
- <style scoped>
- </style>
|