mirror of
https://github.com/duhanbalci/dreport.git
synced 2026-07-01 18:39:16 +00:00
388 lines
10 KiB
Vue
388 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import { defaultAlignForSchema, schemaFormatToFormatType } from '../../../core/schema-parser'
|
|
import type { TableColumn, FormatType } from '../../../core/types'
|
|
|
|
type ItemField = { key: string; title: string; type?: string; format?: string }
|
|
import '../../../styles/properties.css'
|
|
|
|
defineProps<{
|
|
column: TableColumn
|
|
itemFields: ItemField[]
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
update: [colId: string, updates: Partial<TableColumn>]
|
|
remove: [colId: string]
|
|
move: [colId: string, direction: -1 | 1]
|
|
}>()
|
|
</script>
|
|
|
|
<template>
|
|
<div class="tbl-col">
|
|
<!-- Row 1: title + actions -->
|
|
<div class="tbl-col__head">
|
|
<input
|
|
class="tbl-col__title"
|
|
type="text"
|
|
:value="column.title"
|
|
@change="(e) => emit('update', column.id, { title: (e.target as HTMLInputElement).value })"
|
|
:placeholder="column.field"
|
|
data-tip="Sutun basligi"
|
|
/>
|
|
<div class="tbl-col__actions">
|
|
<button class="tbl-col__act" @click="emit('move', column.id, -1)" data-tip="Yukari tasi">
|
|
<svg width="10" height="10" viewBox="0 0 10 10">
|
|
<path d="M5 2L2 6h6L5 2z" fill="currentColor" />
|
|
</svg>
|
|
</button>
|
|
<button class="tbl-col__act" @click="emit('move', column.id, 1)" data-tip="Asagi tasi">
|
|
<svg width="10" height="10" viewBox="0 0 10 10">
|
|
<path d="M5 8L2 4h6L5 8z" fill="currentColor" />
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="tbl-col__act tbl-col__act--del"
|
|
@click="emit('remove', column.id)"
|
|
data-tip="Sutunu sil"
|
|
>
|
|
<svg width="10" height="10" viewBox="0 0 10 10">
|
|
<path
|
|
d="M2 2l6 6M8 2l-6 6"
|
|
stroke="currentColor"
|
|
stroke-width="1.5"
|
|
stroke-linecap="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Row 2: field + align -->
|
|
<div class="tbl-col__controls">
|
|
<select
|
|
v-if="itemFields.length > 0"
|
|
class="tbl-col__field"
|
|
:value="column.field"
|
|
data-tip="Veri alani"
|
|
@change="
|
|
(e) => {
|
|
const field = (e.target as HTMLSelectElement).value
|
|
const node = itemFields.find((f) => f.key === field)
|
|
if (node) {
|
|
emit('update', column.id, {
|
|
field,
|
|
title: node.title,
|
|
align: defaultAlignForSchema(node as any),
|
|
format: schemaFormatToFormatType(node.format),
|
|
})
|
|
} else {
|
|
emit('update', column.id, { field })
|
|
}
|
|
}
|
|
"
|
|
>
|
|
<option v-for="f in itemFields" :key="f.key" :value="f.key">{{ f.key }}</option>
|
|
</select>
|
|
<input
|
|
v-else
|
|
class="tbl-col__field"
|
|
type="text"
|
|
:value="column.field"
|
|
@change="(e) => emit('update', column.id, { field: (e.target as HTMLInputElement).value })"
|
|
data-tip="Veri alani"
|
|
/>
|
|
|
|
<!-- Alignment icons -->
|
|
<div class="tbl-col__align">
|
|
<button
|
|
class="tbl-col__align-btn"
|
|
:class="{ 'tbl-col__align-btn--on': column.align === 'left' }"
|
|
@click="emit('update', column.id, { align: 'left' })"
|
|
data-tip="Sola hizala"
|
|
>
|
|
<svg width="12" height="12" viewBox="0 0 12 12">
|
|
<line x1="1" y1="3" x2="11" y2="3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
<line x1="1" y1="6" x2="8" y2="6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
<line x1="1" y1="9" x2="10" y2="9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="tbl-col__align-btn"
|
|
:class="{ 'tbl-col__align-btn--on': column.align === 'center' }"
|
|
@click="emit('update', column.id, { align: 'center' })"
|
|
data-tip="Ortala"
|
|
>
|
|
<svg width="12" height="12" viewBox="0 0 12 12">
|
|
<line x1="1" y1="3" x2="11" y2="3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
<line x1="2.5" y1="6" x2="9.5" y2="6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
<line x1="1.5" y1="9" x2="10.5" y2="9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="tbl-col__align-btn"
|
|
:class="{ 'tbl-col__align-btn--on': column.align === 'right' }"
|
|
@click="emit('update', column.id, { align: 'right' })"
|
|
data-tip="Saga hizala"
|
|
>
|
|
<svg width="12" height="12" viewBox="0 0 12 12">
|
|
<line x1="1" y1="3" x2="11" y2="3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
<line x1="4" y1="6" x2="11" y2="6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
<line x1="2" y1="9" x2="11" y2="9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Row 3: format + width -->
|
|
<div class="tbl-col__extra" data-tip="Veri gosterim formati">
|
|
<label class="tbl-col__elabel">Format</label>
|
|
<select
|
|
class="tbl-col__fmt"
|
|
:value="column.format ?? ''"
|
|
@change="
|
|
(e) =>
|
|
emit('update', column.id, {
|
|
format: ((e.target as HTMLSelectElement).value || undefined) as FormatType | undefined,
|
|
})
|
|
"
|
|
>
|
|
<option value="">Yok</option>
|
|
<option value="currency">Para birimi</option>
|
|
<option value="number">Sayi</option>
|
|
<option value="date">Tarih</option>
|
|
<option value="percentage">Yuzde</option>
|
|
</select>
|
|
</div>
|
|
<div class="tbl-col__extra" data-tip="Sutun genislik modu">
|
|
<label class="tbl-col__elabel">Genislik</label>
|
|
<select
|
|
class="tbl-col__wtype"
|
|
:value="column.width.type"
|
|
@change="
|
|
(e) => {
|
|
const t = (e.target as HTMLSelectElement).value
|
|
if (t === 'auto') emit('update', column.id, { width: { type: 'auto' } })
|
|
else if (t === 'fr') emit('update', column.id, { width: { type: 'fr', value: 1 } })
|
|
else emit('update', column.id, { width: { type: 'fixed', value: 30 } })
|
|
}
|
|
"
|
|
>
|
|
<option value="auto">Otomatik</option>
|
|
<option value="fixed">Sabit (mm)</option>
|
|
<option value="fr">Oran (fr)</option>
|
|
</select>
|
|
<span
|
|
v-if="column.width.type === 'fixed' || column.width.type === 'fr'"
|
|
class="ts-tip-wrap"
|
|
:data-tip="column.width.type === 'fixed' ? 'Sabit genislik (mm)' : 'Oran degeri (fr)'"
|
|
>
|
|
<input
|
|
class="tbl-col__wval"
|
|
type="number"
|
|
step="1"
|
|
:min="column.width.type === 'fixed' ? 5 : 1"
|
|
:value="(column.width as any).value"
|
|
@change="
|
|
(e) =>
|
|
emit('update', column.id, {
|
|
width: {
|
|
type: column.width.type,
|
|
value:
|
|
parseFloat((e.target as HTMLInputElement).value) ||
|
|
(column.width.type === 'fixed' ? 30 : 1),
|
|
} as any,
|
|
})
|
|
"
|
|
/>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.tbl-col {
|
|
background: #f8fafc;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 5px;
|
|
padding: 5px 6px;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.tbl-col__head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.tbl-col__title {
|
|
flex: 1;
|
|
min-width: 0;
|
|
border: none;
|
|
background: transparent;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
color: #334155;
|
|
padding: 1px 0;
|
|
outline: none;
|
|
}
|
|
|
|
.tbl-col__title:focus {
|
|
border-bottom: 1px solid #93c5fd;
|
|
}
|
|
|
|
.tbl-col__actions {
|
|
display: flex;
|
|
gap: 1px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.tbl-col__act {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 18px;
|
|
height: 18px;
|
|
border: none;
|
|
border-radius: 3px;
|
|
background: transparent;
|
|
color: #94a3b8;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
}
|
|
|
|
.tbl-col__act:hover {
|
|
background: #e2e8f0;
|
|
color: #475569;
|
|
}
|
|
|
|
.tbl-col__act--del:hover {
|
|
background: #fef2f2;
|
|
color: #dc2626;
|
|
}
|
|
|
|
.tbl-col__controls {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.tbl-col__field {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: 2px 4px;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
background: white;
|
|
color: #334155;
|
|
}
|
|
|
|
.tbl-col__field:focus {
|
|
outline: none;
|
|
border-color: #93c5fd;
|
|
}
|
|
|
|
.tbl-col__align {
|
|
display: flex;
|
|
gap: 0;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.tbl-col__align-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 20px;
|
|
height: 20px;
|
|
border: 1px solid #e2e8f0;
|
|
background: white;
|
|
color: #94a3b8;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
}
|
|
|
|
.tbl-col__align-btn:first-child {
|
|
border-radius: 3px 0 0 3px;
|
|
}
|
|
|
|
.tbl-col__align-btn:last-child {
|
|
border-radius: 0 3px 3px 0;
|
|
}
|
|
|
|
.tbl-col__align-btn:not(:first-child) {
|
|
border-left: none;
|
|
}
|
|
|
|
.tbl-col__align-btn--on {
|
|
background: #3b82f6;
|
|
color: white;
|
|
border-color: #3b82f6;
|
|
}
|
|
|
|
.tbl-col__extra {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.tbl-col__elabel {
|
|
font-size: 11px;
|
|
color: #64748b;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.tbl-col__fmt {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: 2px 4px;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
background: white;
|
|
color: #334155;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.tbl-col__wtype {
|
|
width: 80px;
|
|
padding: 2px 4px;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
background: white;
|
|
color: #334155;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.tbl-col__wval {
|
|
width: 36px;
|
|
padding: 2px 3px;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
background: white;
|
|
color: #334155;
|
|
text-align: center;
|
|
-moz-appearance: textfield;
|
|
}
|
|
|
|
.tbl-col__wval::-webkit-inner-spin-button,
|
|
.tbl-col__wval::-webkit-outer-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
|
|
.tbl-col__wval:focus {
|
|
outline: none;
|
|
border-color: #93c5fd;
|
|
}
|
|
|
|
.ts-tip-wrap {
|
|
position: relative;
|
|
display: inline-flex;
|
|
}
|
|
</style>
|